/* pop_timer — логические тики, pause/unlimited, читы и QuickSave state. */ #include #include "tcheck.h" #include "pop_timer.h" /* Минимальный memory-stream вместо EMM. pop_timer_qsave использует ровно * u8/u16, поэтому этот stub одновременно проверяет его endian-контракт. */ static uint8_t stream[3]; static uint8_t stream_pos; void pop_qs_u8(pop_qs_io_t *io, uint8_t *value) { if (io->reading) { if (io->apply) *value = stream[stream_pos]; } else { stream[stream_pos] = *value; } ++stream_pos; } void pop_qs_u16(pop_qs_io_t *io, uint16_t *value) { uint8_t lo, hi; if (io->reading) { lo = stream[stream_pos++]; hi = stream[stream_pos++]; if (io->apply) *value = (uint16_t)(lo | ((uint16_t)hi << 8)); } else { stream[stream_pos++] = (uint8_t)*value; stream[stream_pos++] = (uint8_t)(*value >> 8); } } TC_TEST(timer_starts_and_only_runs_when_allowed) { pop_timer_new_game(); TC_EQ(pop_timer_minutes, POP_TIMER_START_MINUTES); TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); TC_FALSE(pop_timer_tick(0, 1)); /* Settings: unlimited */ TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); TC_FALSE(pop_timer_tick(1, 0)); /* pause/HDD/cutscene */ TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); pop_timer_minutes = 2; pop_timer_ticks = 2; TC_FALSE(pop_timer_tick(1, 1)); TC_EQ(pop_timer_minutes, 2); TC_EQ(pop_timer_ticks, 1); TC_FALSE(pop_timer_tick(1, 1)); TC_EQ(pop_timer_minutes, 1); TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); } TC_TEST(timer_expiry_is_one_shot) { pop_timer_minutes = 1; pop_timer_ticks = 1; TC_TRUE(pop_timer_tick(1, 1)); TC_EQ(pop_timer_minutes, 0); TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); TC_FALSE(pop_timer_tick(1, 1)); TC_FALSE(pop_timer_tick(0, 1)); } TC_TEST(timer_cheats_match_sdlpop_rules) { pop_timer_minutes = 5; pop_timer_cheat_subtract(); TC_EQ(pop_timer_minutes, 4); pop_timer_minutes = 1; pop_timer_cheat_subtract(); TC_EQ(pop_timer_minutes, 1); /* minus не создаёт expiry */ pop_timer_minutes = 0; pop_timer_cheat_add(); TC_EQ(pop_timer_minutes, 1); pop_timer_minutes = 0xFF; pop_timer_cheat_add(); TC_EQ(pop_timer_minutes, 0xFF); /* не сворачиваемся в 0 */ } TC_TEST(timer_qsave_restores_exact_state_and_rejects_bad_tick) { pop_qs_io_t io; pop_timer_minutes = 42; pop_timer_ticks = 103; stream_pos = 0; io.reading = io.apply = io.failed = 0; pop_timer_qsave(&io); TC_EQ(stream_pos, 3); pop_timer_minutes = 7; pop_timer_ticks = 400; stream_pos = 0; io.reading = io.apply = 1; io.failed = 0; pop_timer_qsave(&io); TC_FALSE(io.failed); TC_EQ(pop_timer_minutes, 42); TC_EQ(pop_timer_ticks, 103); stream[0] = 9; stream[1] = 0; stream[2] = 0; /* tick 0 — impossible */ pop_timer_minutes = 33; pop_timer_ticks = 444; stream_pos = 0; io.failed = 0; pop_timer_qsave(&io); TC_TRUE(io.failed); TC_EQ(pop_timer_minutes, 33); TC_EQ(pop_timer_ticks, 444); } void main(void) { TC_RUN(timer_starts_and_only_runs_when_allowed); TC_RUN(timer_expiry_is_one_shot); TC_RUN(timer_cheats_match_sdlpop_rules); TC_RUN(timer_qsave_restores_exact_state_and_rejects_bad_tick); }