Skip to content

Architecture

Владислав edited this page Sep 5, 2026 · 1 revision

Архитектура порта

Оригинал - однооконное Win32/GAPI-приложение: вся игра живёт в WndProc 0x0001fd2c, где WM_TIMER (таймер 10, период 5 мс, заведён в WM_CREATE) делает ввод, физику, камеру и отрисовку. Порт разложил это на модули, но сохранил тот же порядок действий внутри кадра.

Слои

                       jb_main.c                  кадровый цикл, склейка состояний
   ---------------------------------------------
   jb_menu   jb_keyconfig                        экраны
   jb_stage  jb_player  jb_ball  jb_level        игровая логика
   jb_track  jb_trackrow_*                       рендер трассы
   jb_text   jb_gfx  jb_gfx_tile  jb_bmp         рендер и загрузка картинок
   jb_audio  jb_mod  jb_mod_effects              звук
   ---------------------------------------------
   jb_platform_sdl2  jb_touch_sdl2  jb_audio_sdl2  единственный слой, знающий про SDL

Ниже jb_platform.h про SDL не знает никто: игровые модули видят только jb_surface (16-битный буфер) и функции Platform_*. Поэтому порт собирается и под Windows, и под Linux, и под Android без #ifdef в игровой логике.

Модули

Файл Что делает Функция оригинала
jb_main.c разбор аргументов, инициализация, кадровый цикл, HUD Game_Init 0x000113bc, WndProc 0x0001fd2c
jb_platform_sdl2.c окно, back buffer, клавиши, время, файлы, пути Gfx_CreateBackBuffer 0x00021698, Gfx_Present 0x000126fc
jb_touch_sdl2.c сенсорная панель: раскладка, мультитач, отрисовка нет (добавлено портом)
jb_gfx.c ядро блиттера: Blit_Core, цвет, blend, тинт Blit_Core 0x00021a98, Color_Pack16 0x00023bb4
jb_gfx_tile.c масштабирующие блиттеры для тайлов трассы Blit_TileH_Core 0x00022e7c, Blit_TileHV_Core 0x000228e8
jb_bmp.c чтение .bmp в jb_sprite Sprite_LoadResourceBitmap 0x00021a04
jb_assets.c поиск корня ассетов, имена файлов по номеру ресурса LoadBitmapW вызовы Game_Init
jb_appassets.c загрузка всех спрайтов игры одним набором Game_Init 0x000113bc
jb_text.c два шрифта, центрирование, числа Font_Load 0x0001f4d8, Text_DrawCentered 0x00012878
jb_level.c битмап уровня -> сетка тайлов + таблица чекпоинтов Level_LoadTileMap 0x0001261c
jb_stage.c старт уровня, тема, HUD-цвет, таймер, финиш Level_Begin 0x0001376c
jb_player.c ввод, физика мяча, камера WndProc 0x000208f8 (WM_TIMER)
jb_ball.c мяч и тень хвост Track_DrawFrame 0x0001eacc..0x0001ef10
jb_track.c проекционные таблицы и обход 17 строк Track_DrawFrame 0x0001dd54
jb_trackrow_*.c по одному файлу на тему: лёд, трава, лес, небо, «ice alt» TrackRow_Draw* 0x00015b80..0x00019e28
jb_menu.c четыре экрана меню и их клавиши Menu_DrawFrame 0x0001a7d0
jb_keyconfig.c мастер переназначения клавиш, keys.cfg KeyConfig_Tick 0x0001f900
jb_audio.c что играть и с какой громкостью Music_LoadForContext 0x0001db68, Volume_Step 0x00012b54
jb_audio_sdl2.c микшер: 4 слота WAV + музыка hssSpeaker из hss.dll
jb_mod.c, jb_mod_effects.c плеер ProTracker: строки, эффекты, микс hssSpeaker::updateModSFX 0x00104370

Заголовки jb_consts.h, jb_mod_table.h, jb_trackrow.h - только данные: константы с адресами, откуда они прочитаны, и таблица периодов трекера.

Кадровый цикл

jb_main.c main(), цикл while (Platform_PollEvents()):

  1. Время. dt = (ticks - prev_ticks) * 0.001f. В оригинале - g_dtMs 0x00064968 как GetTickCount() - DAT_0006496c, домноженное на 0.001 в 0x00020974.
  2. Ввод. Если активен мастер клавиш - все нажатия уходят ему (KeyConfig_KeyDown), больше ничего не происходит; так же ведёт себя оригинал, проверяя g_inKeyConfig до разбора режима. Иначе PumpKeys() переводит фронты нажатий в действия меню либо в Player_KeyDown / Player_KeyUp.
  3. Логика (только в игровом режиме): Stage_Step крутит часы, Stage_Frozen придерживает физику, пока таймер отрицательный (это пауза перед стартом уровня), затем Player_Step(dt). Приземление поднимает флаг landed, и звук отбоя играется из main, ровно там же, где оригинал звал hssSpeaker::playSound - непосредственно перед записью 0.0 в g_ballY.
  4. Финиш. Stage_Cleared = cam_row > 0x3fe: пауза 1000 мс, поднять max_unlocked, вернуться в меню выбора уровня на следующей позиции.
  5. Отрисовка - ровно один из трёх вариантов: мастер клавиш, меню, игра. Игровой вариант: фон -> Track_DrawFrame -> Ball_DrawFrame -> HUD.
  6. Platform_Present(), затем необязательная выгрузка кадра (dump_path, срабатывает после 1500 мс от старта).

Обратите внимание: порт не ограничивает частоту кадров сам - шаг физики считается от реального dt, а темп задаёт Platform_Present (SDL с вертикальной синхронизацией, если она есть). Оригинал шёл по таймеру 5 мс.

Передача состояния

Игровые модули не держат глобальных состояний друг друга: jb_main.c заводит структуры (jb_player_state, jb_stage, jb_track_state, jb_trackrow_ctx, jb_ball_state, jb_menu, jb_keyconfig) и раздаёт их по вызовам. Поля этих структур - те самые глобалы оригинала, и каждое подписано адресом:

typedef struct {
    float ball_x;        /* g_ballX 0x00064b0c */
    float ball_y;        /* g_ballY 0x00064b10 */
    float ball_row;      /* g_ballRow 0x00064b08 */
    ...
} jb_player_state;

Так проверяемо соответствие «поле порта <-> глобал оригинала», и при расхождении поведения понятно, куда смотреть в дизассемблере.

Clone this wiki locally