• Как поставляются игры на компьютеры?

    @Mercury13
    Программист на «си с крестами» и не только
    Касательно читов. Я простым анализом памяти смог узнать в Microsoft Train Simulator состав поезда игрока, его скорость, положение контроллеров, пройденный путь, давление в различных элементах пневматического тормоза (оказывается, в PSI), боксует ли он. Жаль, я не смог обнаружить привязку всего этого к путям.

    Как я это делал. Через ArtMoney получал некие базовые адреса. Если адрес статический — ну, всё в порядке. Если нет — писал утилиту, в которую изначально вводился найденный адрес. По цифрам я прикидывал, где мог начинаться объект, и снова поиск через ArtMoney… Ну и так далее, пока не дойду до статического адреса. Вот так оно у меня выглядело.

    TmstsLocalTrain = packed record
          _mem0000 : array [$0000..$0061] of byte;
          // 0062
          HeadWagon : dword;   // Головная единица ПС
          // 0066
          TailWagon : dword;   // Хвостовая единица ПС
          // 006A
          LocWagon : dword;    // Управляемая игроком единица ПС
          // 006E
          _mem006E : dword;
          // 0072
          Caps : dword;
          // 0076
          _mem0076 : array [$0076..$0091] of byte;
          // 0092
          Speed : single;       // Скорость по скоростемеру, м/с
          // 0096
          Acceleration : single;  // Ускорение, м/с2
          // 009A
          _mem009A : array [$009A..$00D5] of byte;
          // 00D6
          TimeSec : single;
          // 00DA
          ReversingOdometer : single;
          // 00DE
        end;


    ПС = подвижной состав. Одометр реверсивный, потому что при осаживании (заднем ходе) считает назад. Байтовый массив _mem0000 — это память, которую я не смог опознать. HeadWagon, TailWagon, LocWagon, Caps — на самом деле указатели, но поскольку они не имеют смысла в адресном пространстве лентописателя (задачей было сделать аналог скоростемерной ленты), они Dword, а не указатели.

    Вспоминал, что значат сокращения П, ПТЭ и прочее. Оказалось: паровоз, тепловоз, электровоз. Естественно, регулятор пара есть только у паровозов.
    Ответ написан
    Комментировать
  • Как отличить векторный графический редактор от растрового?

    @Mercury13
    Программист на «си с крестами» и не только
    Векторный
    Растровый с векторными элементами (как и Шоп)
    Растровый
    Растровый
    Векторный

    Собственно, как отличить.
    У растрового — пиксельное поле. Если близко приблизишь, увидишь эти пиксели.
    У векторного — отрезки, кривые и тексты, которые в любой момент можно взять и отредактировать.

    Векторная графика из-за лёгкой редактируемости уже везде в хороших растровых редакторах, типа Шопа и Гимпа. Но они остаются растровыми, потому что основа — всё равно пиксельный холст.
    Ответ написан
  • Как сделать изображение сине-жёлтым?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Для каждого цвета определить положение [0,1] в цветовом континууме. Простейший вариант — вычленить наиболее значимый канал (например, зелёный) → ограничить «крайними» значениями → (возможно) преобразовать в линейное цветовое пространство (сначала [0,255] в [0,1], затем x1/2,2) → провести линейное преобразование.
    2. Провести нелинейное преобразование этого [0,1] → [0,1], если надо.
    3. Преобразовать этот [0,1] в другой континуум. Если работали в линейном цветовом пространстве — преобразовать снова в sRGB, если надо.
    Ответ написан
    Комментировать
  • Можете решать эту задачу?

    @Mercury13
    Программист на «си с крестами» и не только
    Изначально:
    1. У корня глубина 0.

    Команда ADD:
    1. вершина.предок = предок
    2. вершина.глубина = предок.глубина + 1

    Команда GET:
    1. Найти ту вершину, что глубже. Если у одной глубина 5, у другой 7 — подняться от второй на 2.
    2. А теперь параллельно и от первой, и от второй вершины поднимаемся вверх на единицу, пока не придём в одну и ту же вершину.

    Если хватит памяти — можно устроить какой-то кэш. Мой вариант — кэшировать предков порядка 1,2,4,8…
    Чтобы подняться от вершины на 30 единиц…
    1. Находим ближайшую степень двойки (16).
    2. Если эта степень есть в кэше — просто берём её.
    3. Иначе находим максимальную имеющуюся степень (например, 4). Заходим в эту вершину и рекурсивно поднимаемся от неё на 4, потом на 8, заполняя наш кэш.
    4. На оставшиеся 14 поднимаемся по тому же алгоритму.

    Чтобы найти общего предка двух вершин — у одной глубина 30, у другой 40.
    1. От той вершины, у которой 40, поднимаемся на 10 единиц. Алгоритм я привёл.
    2. Одинаковые? — тогда понятно.
    3. Имеют общего предка? — тогда понятно.
    4. Иначе поднимаемся на 1, 2, 4, 8, 16 единиц от каждой из них по указанному алгоритму, каждый из этих шагов при наличии кэша даст O(1). Находим последнюю НЕСОВПАДАЮЩУЮ глубину. Для них повторяем алгоритм.

    ВАРИАНТ КЭША ВТОРОЙ. Для вершины глубины 27=11010 кэшировать предков АБСОЛЮТНЫХ (то есть от корня!) глубин 16=1.0000, 24=11.000, 24 :) =110.00, 26=1101.0. Тогда при создании вершины весь кэш достаточно быстро строится на основе кэша вершины-предка.

    Чтобы подняться от вершины глубины 27 на расстояние, например, 5, нам нужна абсолютная глубина 27−5=22.
    Есть только 24 → действуем так. Выныриваем туда на 24, но там кэш совершенно негодный → а дальше нас проведёт кэш глубины 23. Так поднимаемся на 1 до 23-й и повторяем алгоритм.

    Чтобы найти общего предка двух вершин (их глубины, скажем, 27 и 42), точно так же уравниваем глубины. Затем поднимаемся до глубин 16, 24, 24, 26…, пока не найдём несовпадение. Идём в эти несовпадающие вершины, проходим от них к предку и повторяем алгоритм.
    Ответ написан
  • Какова будет грамматика для данного языка?

    @Mercury13
    Программист на «си с крестами» и не только
    L → EZUU M ZUUUUF
    M → ZUU M ZUUUU | S
    Для S грамматику вы уже придумали.
    Затем — не буду расписывать, они многословны, но просты — UZ → ZU

    А чтобы превратить Z в 0 и U в единицу, если i,n ∊ N+…
    Сделаем затравку…
    EZ → E0
    Ua → 1a
    cZ → c0
    UF → 1F
    …Уничтожим технические нетерминалы…
    E0 → 0
    1F → 1
    …И проведём волну!
    0Z → 00
    U1 → 11

    Вроде так.
    (Z = zero, U = unit)
    Ответ написан
    Комментировать
  • Как в языке си вернуть пустой массив?

    @Mercury13
    Программист на «си с крестами» и не только
    Если возвращаем НОВУЮ память:
    1. вернуть массив заведомо бóльшей длины и поле длины.
    struct Arr1 {
      int length;
      int data[100];
    }
    struct Arr1 someFunc() {}

    2. Вернуть динамический массив.
    struct Arr2 {
      int length;
      int* data;   // не забудь free(arr.data);
    }
    struct Arr2 someFunc() {}


    Если работаем в ИМЕЮЩЕЙСЯ памяти.
    3. Тот же Arr2, но разница в том, что data не надо высвобождать.
    4. А если arr.data гарантированно понятное — то можно вернуть только int, поле длины (как в функции sprintf).
    int sprintf ( char * str, const char * format, ... );

    Она и возвращает массив — только его адрес гарантированно будет str и вернуть надо только длину.
    Ответ написан
    Комментировать
  • Как понять каких пакетов не хватает для корректной работы Qt программы?

    @Mercury13
    Программист на «си с крестами» и не только
    sqldrivers\qsqlmysql.dll (именно так, в подкаталоге sqldrivers!!)
    Qt5/6Sql.dll

    А дальше залезьте Dependency Walker’ом в драйвер qsqlmysql.dll и посмотрите, чего не хватает.
    Одни сборки требуют официальный коннектор MySQL, другие от MariaDB.
    Дополнительные DLL’ки должны лежать в каталоге с exe.

    Я отказался от Qt MySQL именно потому, что при выпуске легко накосячить — а поскольку MySQL не основная фича, это очень не скоро заметят. Написал собственную горбушку, к тому же коннектящуюся к MySQL8.
    Ответ написан
    2 комментария
  • Почему #pragma comment (lib) не обрабатывается?

    @Mercury13
    Программист на «си с крестами» и не только
    Судя по украшению __imp и расширеню *.o, вы прагму для VS применяете на MinGW. Поскольку я не знаю, на какой вы системе программирования, заходите в настройки проекта и подключаете билиотеку ws2_32.
    Ответ написан
    Комментировать
  • Где лучше хранить массив объектов? json или бд?

    @Mercury13
    Программист на «си с крестами» и не только
    Хранение лучше в БД. Любой, хоть SqLite, хоть MyISAM, нет у вас каких-то причин искать СамуюМоднуюСУБД®.

    Как промежуточный механизм передачи данных можно и JSON, если так хотите. Всё зависит от ваших навыков веб-разработчика. Поскольку мои веб-знания устарели лет этак на пятнадцать, я бы сначала сделал чистый PHP + не-AJAX, а потом думал бы.
    Ответ написан
    Комментировать
  • Как исправить ошибку при компиляции C++ кода?

    @Mercury13
    Программист на «си с крестами» и не только
    Тут поцапались макрос Verify в dbg.h и функция Verify в cryptlib.h.
    Я бы предпочёл переименовать одно из двух — лучше макрос — а затем разобрать, где должен быть макрос, а где функция.

    Виноват определённо разработчик макроса: он назвал его простым и распространённым словом Verify. Функция-то где-то внутри объекта, а макрос заменяет идентификатор Verify на всякую дрянь независимо от того, где этот идентификатор и в каком пространстве имён. Кроме того, подобные макросы принято называть как-нибудь DBG_VERIFY — если только это не макрос совместимости, подменяющий, например, более позднее nullptr в старое NULL.
    Ответ написан
    23 комментария
  • Как использовать RATE_VERY_FAST (Java, Android)?

    @Mercury13
    Программист на «си с крестами» и не только
    Давайте посмотрим, что представляет собой RATE_XXX и SENSOR_DELAY_XXX.
    SENSOR_DELAY_FASTEST = 0
    SENSOR_DELAY_GAME = 1
    SENSOR_DELAY_UI = 2
    SENSOR_DELAY_NORMAL = 3

    RATE_STOP = 0
    RATE_NORMAL = 1
    RATE_FAST = 2
    RATE_VERY_FAST = 3

    Функция registerListener требует задержку в мкс, или одну из четырёх констант SENSOR_DELAY. Поставив RATE_VERY_FAST=3, вы реально поставили ему SENSOR_DELAY_NORMAL=3!

    А куда же совать RATE? В SensorDirectChannel.configure! А сам SensorDirectChannel можно получить через SensorManager.createDirectChannel! Вот как-то так — и даже понимаю, откуда такая архитектура. Ну не может система, оптимизированная под низкое энергопотребление, 1700 раз в секунду пинать пользовательскую программу — программа получает управление намного реже, а информация с акселерометра накапливается в буфере памяти.

    UPD. Есть и вторая причина. Обработка может затянуться более чем на 1/1700 секунды, и в это время датчик продолжит писать информацию в тот самый буфер.

    UPD2. Другими словами, через callback и через буфер в памяти — две разных архитектуры. Первая обеспечит низкую задержку. Вторая — высокую (причём стабильно высокую!) частоту опроса, но задержка от регистрации до обработки, скорее всего, будет побольше.
    Ответ написан
    2 комментария
  • QAbstractItemModel::beginInsertRows: что происходит, если вставка не удалась?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Я пока придумал такую конструкцию…
    class Committer {  // interface
    public:
      virtual void commit() = 0;
      virtual ~Committer() = default;
    }
    
    enum class ErrCode { OK, UNCLONEABLE, BAD_RECIPIENT };
    
    struct CommitObj {
      ErrCode errCode;
      std::unique_ptr<Committer> action;
    }
    
    class UiObj {
    public:
      virtual CommitObj startClone(std::shared_ptr<UiObj> recipient) = 0;
    }


    Мы или получаем объект, для которого а защищённом блоке достаточно дать commit(), или причину, почему клонирование невозможно.
    Ответ написан
    Комментировать
  • Как сделать, чтобы шаблонная функция принимала const char*?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    template <class T> struct string_traits;
    
        template <class C, class T, class A>
        struct string_traits<std::basic_string<C,T,A>> {
            using Ch = C;
            using Tr = T;
            using Sv = std::basic_string_view<C,T>;
        };
    
        template <class C, class T>
        struct string_traits<std::basic_string_view<C,T>> {
            using Ch = C;
            using Tr = T;
            using Sv = std::basic_string_view<C,T>;
        };
    
        template <class C>
        struct string_traits<const C*> {
            using Ch = C;
            using Tr = std::char_traits<C>;
            using Sv = std::basic_string_view<C>;
        };
    
        template <class C, size_t N>
        struct string_traits<C[N]> {
            using Ch = C;
            using Sv = std::basic_string_view<C>;
        };
    
        template<class S>
        using s_v_t = typename string_traits<std::remove_cvref_t<S>>::Sv;
    
        namespace detail {
            template <class Sv>
            Sv remainderSv(Sv s, Sv prefix)
            {
                if (s.length() <= prefix.length()
                        || !s.starts_with(prefix))
                    return {};
                return s.substr(prefix.length(), s.length() - prefix.length());
            }
        }
    
        /// @brief remainderSv
        ///    Same for prefix only
        template <class A>
        inline auto remainderSv(const A& s, s_v_t<A> prefix) -> s_v_t<A>
            { return detail::remainderSv<s_v_t<A>>(s, prefix); }
    Ответ написан
    Комментировать
  • Ковариантность возвращаемых типов в Javа, я правильно понял суть?

    @Mercury13
    Программист на «си с крестами» и не только
    Ковариантность начинается, когда мы делаем class BuildCircle extends BuildShape.
    (Лучше BuildShape оформить как интерфейс, а не как класс, но шут с ним.)

    Ковариантность связана с принципом подстановки Лисков: чтобы потомок вписывался в контракт, установленный предком, он может ужесточать требования к себе (скажем, выдавать более узкий тип) и ослаблять требования к другим (скажем, принимать поток реального времени вроде консоли/сокета — а не только файл, который знает себе длину и позволяет перемотку).

    Вот это «выдавать более узкий тип, чем полагает предок» — и есть ковариантность.

    Обратное — принимать поток реального времени, а не только файл — называется контравариантность. Насколько мне известно, в Java на уровне языка её нет, но какие-то части ухитряются делать через шаблоны.
    Ответ написан
    3 комментария
  • При наличии каких членов класса move-конструктор не будет сгенерирован автоматически?

    @Mercury13
    Программист на «си с крестами» и не только
    Implicitly-declared move constructor

    If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
    • there are no user-declared copy constructors;
    • there are no user-declared copy assignment operators;
    • there are no user-declared move assignment operators;
    • there is no user-declared destructor.

    then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).

    Ну и пользовательский move-конструктор исключает автоматический.
    Так что 2, 3, 4, 5, 6.
    Ответ написан
    Комментировать
  • Защищаются ли алгоритмы авторским правом?

    @Mercury13
    Программист на «си с крестами» и не только
    Тут два камня преткновения.
    1. В некоторых юрисдикциях через большую задницу, но можно защитить алгоритм патентом. Если алгоритм известен уже 20 лет или несколько фирм им вовсю пользуются — забивайте.
    2. Если, как оказалось, вы у этого разработчика работаете, вы гарантированно полетите с работы с волчьим билетом, да и в трудовом договоре может найтись крючок.
    Ответ написан
    Комментировать
  • Как преобразовать std::string в LPCSTR?

    @Mercury13
    Программист на «си с крестами» и не только
    std::string dir = "subdir";
    CreateDirectoryA(dir.c_str(), nullptr);
    Ответ написан
    Комментировать
  • Как создать новый экземпляр класса переменной?

    @Mercury13
    Программист на «си с крестами» и не только
    1. В Си++ нет такого понятия, как виртуальный конструктор. А виртуальный конструктор копирования — я вообще не понимаю…
    2. В makecopy вы допустили ошибку — переменная никуда не девается и тупо занимает память.
    3. Чтобы отдать указатель на переменную и сказать: «сам уничтожай, когда хочешь»,— есть стандартный тип unique_ptr. Но для ковариантности по unique_ptr нужны две функции: одна виртуальная и возвращает простой указатель, другая отдаёт конкретный u_p.

    Для простоты предполагаю, что все классы неабстрактные, и виртуальная vclone имеет права доступа public, а не protected.
    #include <iostream>
    #include <memory>
    
    #define IMPLEMENT_CLONE_1(Class, Keyword) \
        virtual Class* vclone() const Keyword { return new Class(*this); }  \
        auto clone() const { return std::unique_ptr<Class>{vclone()}; }
    
    #define IMPLEMENT_CLONE(Class)  IMPLEMENT_CLONE_1(Class, override)
    
    class BaseClass {
    public:
      IMPLEMENT_CLONE_1(BaseClass, )
      virtual ~BaseClass() = default;
    };
    
    class MyClass : public BaseClass {
    public:
      IMPLEMENT_CLONE(MyClass)
      void print() const { std::cout << "I'm a MyClass!" << std::endl; }
    };
    
    int main()
    {
      auto a = std::make_unique<MyClass>();
      auto b = a->clone();
      b->print();
      return 0;
    }


    Есть ещё вот такая хрѣнь. Я уж не знаю, что лучше: сóрок пя́ток или пятóк сорóк прямое преобразование указателей или препроцессор.

    #include <iostream>
    #include <memory>
    
    class BaseClass {
    public:
        auto clone() const { return std::unique_ptr<BaseClass>{ vclone() }; }
    protected:
        virtual BaseClass* vclone() const { return new BaseClass(*this); }
    };
    
    template <class Super, class This>
    class CloneImpl : public Super
    {
    public:
        auto clone() const { return std::unique_ptr<This>{ static_cast<This*>(vclone()) }; }
    protected:
        CloneImpl* vclone() const override { return new This(*static_cast<const This*>(this)); }
    };
    
    class MyClass : public CloneImpl<BaseClass, MyClass> {
    public:
      void print() const { std::cout << "I'm a MyClass!" << std::endl; }
    };
    
    int main()
    {
        auto a = std::make_unique<MyClass>();
        auto b = a->clone();
        b->print();
        return 0;
    }
    Ответ написан
  • Почему у меня %c выводит а %s выбивает ошибку?

    @Mercury13
    Программист на «си с крестами» и не только
    char x = 'FIO';
    Ошибка.
    Многосимвольные константы определяются реализацией, используются для жёсткой оптимизации, и в Visual C++ крайне ненадёжны. В GCC вроде бэ-мэ, но в любом случае преобразование в char 'FIO', которое должно занимать три байта, даст 'F' или 'O'.
    Вам нужна не символьная константа, а строковая:
    char x[] = "FIO";
    И, соответственно, %s.

    А как действует printf и любая другая функция с переменным числом аргументов? Эти аргументы сваливаются навалом в область памяти, которая называется «стек вызовов», и printf начинает этот навал разбирать. Чтобы сдвинуло указатель на один байт и сказало: это char — и используется формат %c. Разумеется, неверный формат приведёт к неверной интерпретации типа данных и отказу на одной или всех платформах.
    %c — в памяти лежит один байт, интерпретировать его как один символ
    %s — в памяти лежит указатель на строку, заканчивающуюся нулём, 4/8 байтов. (Все массивы в исходном Си куда бы то ни было передаются как указатели.)
    Ответ написан
    3 комментария
  • Как правильно выделить память (с проверкой выделения) для массива класса?

    @Mercury13
    Программист на «си с крестами» и не только
    Но обычный new выкидывает std::bad_alloc.
    Так что не совсем понятно, что вы хотите сделать, когда делаете new с флагом «не кидать», а потом при неудавшемся new кидаете аварию.
    Ответ написан
    Комментировать