Repository navigation
Architecture
Оригинал - однооконное 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()):
-
Время.
dt = (ticks - prev_ticks) * 0.001f. В оригинале -g_dtMs 0x00064968какGetTickCount() - DAT_0006496c, домноженное на0.001в0x00020974. -
Ввод. Если активен мастер клавиш - все нажатия уходят ему
(
KeyConfig_KeyDown), больше ничего не происходит; так же ведёт себя оригинал, проверяяg_inKeyConfigдо разбора режима. ИначеPumpKeys()переводит фронты нажатий в действия меню либо вPlayer_KeyDown/Player_KeyUp. -
Логика (только в игровом режиме):
Stage_Stepкрутит часы,Stage_Frozenпридерживает физику, пока таймер отрицательный (это пауза перед стартом уровня), затемPlayer_Step(dt). Приземление поднимает флагlanded, и звук отбоя играется изmain, ровно там же, где оригинал звалhssSpeaker::playSound- непосредственно перед записью0.0вg_ballY. -
Финиш.
Stage_Cleared=cam_row > 0x3fe: пауза 1000 мс, поднятьmax_unlocked, вернуться в меню выбора уровня на следующей позиции. -
Отрисовка - ровно один из трёх вариантов: мастер клавиш, меню, игра.
Игровой вариант: фон ->
Track_DrawFrame->Ball_DrawFrame-> HUD. -
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;Так проверяемо соответствие «поле порта <-> глобал оригинала», и при расхождении поведения понятно, куда смотреть в дизассемблере.
Устройство порта
Сборка и происхождение