/* * pop_level.c — загрузка уровня PoP из сырого res200N.bin (Фаза L1). * См. pop_level.h. Порт формата blueprnt DAT 1.0 (Table 6 POP-DAT). */ #include #include #include #include #include /* gfx_w0_map / gfx_w0_unmap */ #include "pop_level.h" /* ISR-стаб W0-страницы (тот же приём, что atlas_load): при прерывании с * замапленной в W0 страницей CPU прыгает на 0x0038 → там обязан быть JP на * _gfx_w0_isr (восстановит окно). Символ внутренний для libbgi, но * стабильный — линкер тянет его из bgi256.lib (графика уже подключена). */ extern void _gfx_w0_isr(void); #define LVL_DATA_OFF 0x100u /* данные с 0x100; 0x00..0xFF — ISR-стаб */ /* Смещения блоков blueprnt относительно начала данных (Table 6). */ #define BP_FG 0u #define BP_BG 720u #define BP_LINKS 1952u #define BP_START 2112u #define ROOM_TILES 30u #define MIN_SIZE (BP_START + 3u) /* минимум, чтобы прочитать старт */ static uint8_t lvl_blk, lvl_page, lvl_ok; int pop_level_load(const char *path) { uint8_t *pg = (uint8_t *)0xC000; /* страница мапится в W3 на время чтения */ uint8_t saved_w3, blk; uint16_t stub; int fd, n; fd = open(path, O_RDONLY); if (fd < 0) return -1; blk = mem_alloc_pages(1); if (!blk) { close(fd); return -1; } lvl_blk = blk; lvl_page = mem_get_page(blk, 0); saved_w3 = _io_page_w3; sprinter_page_w3(lvl_page); /* Файл целиком в offset 0x100 (ESTEX READ пишет в W3 — как atlas_load). */ n = read(fd, pg + LVL_DATA_OFF, 16384 - LVL_DATA_OFF); if (n >= (int)MIN_SIZE) { /* пропатчить ISR-стаб */ stub = (uint16_t)&_gfx_w0_isr; pg[0x38] = 0xC3; /* JP _gfx_w0_isr */ pg[0x39] = (uint8_t)(stub & 0xFF); pg[0x3A] = (uint8_t)(stub >> 8); pg[0x66] = 0xED; pg[0x67] = 0x45; /* RETN (NMI-хвост) */ } sprinter_page_w3(saved_w3); close(fd); if (n < (int)MIN_SIZE) { mem_free_block(blk); return -1; } lvl_ok = 1; return 0; } void pop_level_free(void) { if (lvl_ok) { mem_free_block(lvl_blk); lvl_ok = 0; } } /* Указатель на fg-тайлы комнаты r (1..24) в W0-пространстве (страница уже * замаплена вызывающим). */ static const uint8_t *room_fg_ptr(uint8_t r) { return (const uint8_t *)(LVL_DATA_OFF + BP_FG) + (uint16_t)(r - 1) * ROOM_TILES; } static const uint8_t *room_bg_ptr(uint8_t r) { return (const uint8_t *)(LVL_DATA_OFF + BP_BG) + (uint16_t)(r - 1) * ROOM_TILES; } int pop_room_load(uint8_t room, uint8_t *fg, uint8_t *bg, uint8_t *lcol_fg, uint8_t *lcol_bg, uint8_t *below_fg) { const uint8_t *rfg, *rbg, *lnk; uint8_t left, down, i; if (!lvl_ok || room < 1 || room > 24) return -1; gfx_w0_map(lvl_page); rfg = room_fg_ptr(room); rbg = room_bg_ptr(room); lnk = (const uint8_t *)(LVL_DATA_OFF + BP_LINKS) + (uint16_t)(room - 1) * 4; for (i = 0; i < ROOM_TILES; i++) { fg[i] = (uint8_t)(rfg[i] & 0x1F); /* тип тайла (верх. биты — модиф., пока не нужны) */ bg[i] = rbg[i]; /* backtable как есть */ } left = lnk[0]; /* L */ down = lnk[3]; /* D */ /* leftcol: правый столбец (col9) комнаты слева, строки 0..2 */ if (left >= 1 && left <= 24) { const uint8_t *lf = room_fg_ptr(left); const uint8_t *lb = room_bg_ptr(left); for (i = 0; i < 3; i++) { lcol_fg[i] = (uint8_t)(lf[i * 10 + 9] & 0x1F); lcol_bg[i] = lb[i * 10 + 9]; } } else { for (i = 0; i < 3; i++) { lcol_fg[i] = 20; lcol_bg[i] = 0; } /* нет комнаты = стена */ } /* belowrow: верхний ряд (row0) комнаты снизу */ if (down >= 1 && down <= 24) { const uint8_t *df = room_fg_ptr(down); for (i = 0; i < 10; i++) below_fg[i] = (uint8_t)(df[i] & 0x1F); } else { for (i = 0; i < 10; i++) below_fg[i] = 20; /* нет комнаты снизу = стена */ } gfx_w0_unmap(); return 0; } uint8_t pop_room_link(uint8_t room, uint8_t side) { uint8_t v; if (!lvl_ok || room < 1 || room > 24 || side > 3) return 0; gfx_w0_map(lvl_page); v = ((const uint8_t *)(LVL_DATA_OFF + BP_LINKS))[(uint16_t)(room - 1) * 4 + side]; gfx_w0_unmap(); return v; } static uint8_t start_byte(uint16_t off) { uint8_t v; if (!lvl_ok) return 0; gfx_w0_map(lvl_page); v = ((const uint8_t *)(LVL_DATA_OFF))[off]; gfx_w0_unmap(); return v; } uint8_t pop_level_start_room(void) { return start_byte(BP_START + 0); } uint8_t pop_level_start_pos(void) { return start_byte(BP_START + 1); } int8_t pop_level_start_dir(void) { return (int8_t)start_byte(BP_START + 2); }