/* * pop_arc.c — чтение архива ресурсов PBA1, БАНК 8. * * Контракт и мотивация — в pop_arc.h. Здесь всё сводится к трём вызовам * libc: open один раз на группу, lseek к элементу и bank_read_page прямо в * EMM-страницу (тот резидентен и потому безопасен из банка huge — он же * переключает W3, где живёт наш код). */ #include #include #include #include #include #include "pop_arc.h" #include "_pop_file.h" /* Пути 8.3, как требует DSS. Держим их ЗДЕСЬ: строка обязана лежать в том * же банке, что и код, который её читает (см. pop_arc.h). */ static const char *const arc_path[POP_ARC_IDS] = { "BG\\bg.arc", "KID\\kid.arc", "GUARD\\guard.arc", "SKEL\\skel.arc", "VIZIER\\vizier.arc", "SHADOW\\shadow.arc", "TITLE\\title.arc", "PV\\pv.arc", "SND\\snd.arc" }; int8_t pop_arc_open_id(pop_arc_t *a, uint8_t id, uint8_t *tbl, uint8_t tbl_max) __banked { if (id >= POP_ARC_IDS) return -1; return pop_arc_open(a, arc_path[id], tbl, tbl_max); } int8_t pop_arc_open(pop_arc_t *a, const char *path, uint8_t *tbl, uint8_t tbl_max) __banked { uint8_t head[8]; int fd; a->fd = -1; a->count = 0; POP_PATH_CALL(fd, open(path, O_RDONLY)); if (fd < 0) return -1; if (read(fd, head, sizeof(head)) != (int)sizeof(head) || memcmp(head, "PBA1", 4) != 0 || head[4] == 0 || head[4] > tbl_max || head[4] > POP_ARC_MAX) { close(fd); return -1; } if (read(fd, tbl, (uint16_t)head[4] * POP_ARC_REC) != (int)((uint16_t)head[4] * POP_ARC_REC)) { close(fd); return -1; } a->fd = fd; a->count = head[4]; return (int8_t)head[4]; } uint16_t pop_arc_size(const uint8_t *tbl, uint8_t idx) { const uint8_t *rec = tbl + (uint16_t)idx * POP_ARC_REC; return (uint16_t)(rec[2] | ((uint16_t)rec[3] << 8)); } int pop_arc_read(pop_arc_t *a, const uint8_t *tbl, uint8_t idx, uint16_t skip, uint8_t page, uint16_t off, uint16_t size) __banked { const uint8_t *rec; uint16_t sec, len; long pos; if (a->fd < 0 || idx >= a->count) return -1; rec = tbl + (uint16_t)idx * POP_ARC_REC; sec = (uint16_t)(rec[0] | ((uint16_t)rec[1] << 8)); len = (uint16_t)(rec[2] | ((uint16_t)rec[3] << 8)); if (skip >= len) return 0; len = (uint16_t)(len - skip); if (size && size < len) len = size; /* Сектора в байты — единственное место, где нужна 32-битная величина: * архив может быть больше 64 КБ, и lseek принимает long. Путь холодный * (загрузка ресурсов), в кадре этого нет. */ pos = ((long)sec << 9) + (long)skip; if (lseek(a->fd, pos, SEEK_SET) < 0) return -1; return bank_read_page(a->fd, page, off, len); } int8_t pop_arc_atlas(pop_arc_t *a, const uint8_t *tbl, uint8_t idx, atlas_t *at) __banked { uint8_t blk, page; int n; blk = mem_alloc_pages(1); if (!blk) return -1; page = mem_get_page(blk, 0); n = pop_arc_read(a, tbl, idx, 0, page, 0, 0); /* Дальше всё общее с atlas_load: магия 'SPA1', разбор заголовка и * gfx_w0_page_prepare. При ошибке atlas_attach сам отпускает блок. */ return (int8_t)atlas_attach(at, blk, page, n); } void pop_arc_close(pop_arc_t *a) __banked { if (a->fd >= 0) close(a->fd); a->fd = -1; a->count = 0; }