From 17a371fdca1724e5c453f3a0fca6fbddcdc7e3a2 Mon Sep 17 00:00:00 2001 From: Miloslav Ciz Date: Sun, 19 Jan 2025 22:19:44 +0100 Subject: [PATCH] Start replay loading --- assets.h | 2 +- game.h | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++---- general.h | 22 ++++++++++ racing.h | 36 +++++++++++++--- 4 files changed, 169 insertions(+), 16 deletions(-) diff --git a/assets.h b/assets.h index d513f01..09cf3da 100644 --- a/assets.h +++ b/assets.h @@ -85,7 +85,7 @@ static const char *LCR_internalResourceFile = " map end " "#Mmap2;4321 1 :*H1k0J :,s0s0 :fd190 " - "#Rtestrepl;aaa#Rrepl2;" + "#Rrep1;testmap;482f70f9 00000188:00c1:0089:0111:00b9:0091:0109:0028:0050:00c1:0093:0030:00d1:0069:0041:0020:0071:0013:0012:0023:0022:0050:0032:0020:0022:0060:0024:00bc:0044" "#Mmap3;4321 1 :*H1k0J :,s0s0 :fd190 " "#Mmap4;4321 1 :*H1k0J :,s0s0 :fd190 " "#Mmap5;4321 1 :*H1k0J :,s0s0 :fd190 " diff --git a/game.h b/game.h index 1696b0b..49da67d 100644 --- a/game.h +++ b/game.h @@ -175,7 +175,11 @@ uint8_t LCR_gameGetNextAudioSample(void); #define LCR_GAME_STATE_RUN_STARTING 0x01 #define LCR_GAME_STATE_RUN 0x02 #define LCR_GAME_STATE_RUN_FINISHED 0x03 -#define LCR_GAME_STATE_LOADING_MAP 0x04 + +#define LCR_GAME_STATE_LOADING_RUN 0x04 +#define LCR_GAME_STATE_LOADING_REP1 0x05 +#define LCR_GAME_STATE_LOADING_REP2 0x06 + #define LCR_GAME_STATE_END 0xff // forward decls of pixel drawing functions for the renderer @@ -422,7 +426,7 @@ void LCR_seekResourceByIndex(unsigned int index, char magicNumber) } while (c); } -void LCR_gameStartRun(unsigned int mapIndex) +void LCR_gameLoadMap(unsigned int mapIndex) { char mapName[LCR_MAP_NAME_MAX_LEN]; @@ -443,7 +447,83 @@ void LCR_gameStartRun(unsigned int mapIndex) } LCR_mapLoadFromStr(LCR_gameGetNextResourceStrChar,mapName); - LCR_gameSetState(LCR_GAME_STATE_LOADING_MAP); +} + +/** + Loads replay by its index, returns index of a map for the replay (and the map + will be loaded as with LCR_mapLoadFromStr) or -1 if the map wasn't found or -2 + if the replay couldn't be loaded. This function potentially reloads current + map! +*/ +unsigned int LCR_gameLoadReplay(unsigned int replayIndex) +{ + uint32_t mapHash; + uint16_t nameHash; + char c; + + LCR_LOG1("loading replay and map"); + + LCR_seekResourceByIndex(replayIndex,'R'); + + do // skip name + { + c = LCR_gameGetNextResourceFileChar(); + } + while (c && c != LCR_RESOURCE_FILE_SEPARATOR2 && + c != LCR_RESOURCE_FILE_SEPARATOR); + + if (!LCR_replayLoadFromStr(LCR_gameGetNextResourceStrChar, + &mapHash,&nameHash)) + return -2; + + // now try to find the map with given nameHash + LCR_gameRewindResourceFile(); + + unsigned int skipTo = 0; + + while (1) + { + unsigned int mapIndex = 0; + + while (1) // find first skipToth map + { + c = LCR_gameGetNextResourceFileChar(); + + if (c == 0) + return -1; + else if (c == 'M') + { + if (mapIndex >= skipTo && + nameHash == _LCR_simpleStrHash(LCR_gameGetNextResourceStrChar,';')) + { + LCR_LOG2("map name hash matches"); + LCR_gameLoadMap(mapIndex); + + if (mapHash == LCR_currentMap.hash) + return mapIndex; + else + { + LCR_LOG2("map hash doesn't match"); + // map hash doesn't match + skipTo = mapIndex + 1; + break; + } + } + + mapIndex++; + } + + while (c != LCR_RESOURCE_FILE_SEPARATOR) + { + if (c == 0) + return -1; + + c = LCR_gameGetNextResourceFileChar(); + } + } + } + + return 0; } void LCR_gameEraseMenuItemNames(void) @@ -608,6 +688,13 @@ void LCR_gameTimeToStr(uint32_t timeMS, char *str) str[2] = '\''; } +int _LCR_gameIsLoading(void) +{ + return + (LCR_game.state == LCR_GAME_STATE_LOADING_RUN) || + (LCR_game.state == LCR_GAME_STATE_LOADING_REP1) || + (LCR_game.state == LCR_GAME_STATE_LOADING_REP2); +} void LCR_gameDraw3DView(void) { @@ -808,10 +895,31 @@ void LCR_gameHandleInput(void) break; case 1: - LCR_gameStartRun( - LCR_game.resourceFile.firstItemIndex + LCR_game.menu.selectedItem); + LCR_gameLoadMap(LCR_game.resourceFile.firstItemIndex + + LCR_game.menu.selectedItem); + LCR_gameSetState(LCR_GAME_STATE_LOADING_RUN); break; + case 2: + case 3: + { + int mapIndex = LCR_gameLoadReplay(LCR_game.resourceFile.firstItemIndex + + LCR_game.menu.selectedItem); + + if (mapIndex < -1) + { + LCR_LOG1("couldn't load replay"); + } + else if (mapIndex == -1) + { + LCR_LOG1("couldn't load replay map"); + } + else + LCR_gameSetState(LCR_GAME_STATE_LOADING_REP1); + + break; + } + default: break; } } @@ -901,7 +1009,7 @@ uint8_t LCR_gameStep(uint32_t time) LCR_game.keyStates[i] = LCR_keyPressed(i) ? (LCR_game.keyStates[i] < 255 ? LCR_game.keyStates[i] + 1 : 255) : 0; - if (LCR_game.state == LCR_GAME_STATE_LOADING_MAP) + if (_LCR_gameIsLoading()) { LCR_rendererLoadMap(); LCR_gameResetRun(LCR_racing.playingReplay); @@ -975,8 +1083,7 @@ LCR_replayOutputStr(_LCR_gameResourceCharWrite); while (time >= LCR_game.nextRenderFrameTime) LCR_game.nextRenderFrameTime += 1000 / LCR_SETTING_FPS; - if (LCR_game.state == LCR_GAME_STATE_MENU || - LCR_game.state == LCR_GAME_STATE_LOADING_MAP) + if ((LCR_game.state == LCR_GAME_STATE_MENU) || _LCR_gameIsLoading()) LCR_rendererDrawMenu(LCR_texts[LCR_TEXTS_TABS + LCR_game.menu.selectedTab],LCR_game.menu.itemNamePtrs, LCR_game.menu.itemCount + 1,LCR_game.menu.selectedItem); @@ -989,7 +1096,7 @@ LCR_replayOutputStr(_LCR_gameResourceCharWrite); sleep = tmp < sleep ? tmp : sleep; } - if (LCR_game.state == LCR_GAME_STATE_LOADING_MAP) + if (_LCR_gameIsLoading()) { // show the "loading" screen diff --git a/general.h b/general.h index da25b71..29b2391 100644 --- a/general.h +++ b/general.h @@ -42,4 +42,26 @@ int _LCR_hexDigitVal(char c) return -1; } +/** + Computes simple hash of a string represented by a function returning next + string character, ending at 0 or endChar. This is intended for simple (but + not 100% reliable) string comparison. +*/ +uint16_t _LCR_simpleStrHash(char (*nextChar)(void), char endChar) +{ + uint16_t r = 0; + + while (1) + { + char c = nextChar(); + + if (c == 0 || c == endChar) + break; + + r = ((r << 5) | (r >> 11)) + c; + } + + return r; +} + #endif // guard diff --git a/racing.h b/racing.h index 8f0b85f..f7325e5 100644 --- a/racing.h +++ b/racing.h @@ -47,8 +47,6 @@ typedef int32_t LCR_GameUnit; ///< abstract game unit #include "map.h" #include "tinyphysicsengine.h" -// TODO: move some of this to constants? - #define LCR_GRAVITY (LCR_PHYSICS_UNIT / 160) #define LCR_FAN_FORCE 3 #define LCR_FAN_FORCE_DECREASE 6 @@ -174,8 +172,8 @@ void LCR_replayOutputStr(void (*printChar)(char)) for (int i = 0; i < 8; ++i) { - printChar(_LCR_hexDigit(hash % 16)); - hash /= 16; + printChar(_LCR_hexDigit((hash >> 28) % 16)); + hash <<= 4; } printChar(' '); @@ -204,15 +202,24 @@ void LCR_replayOutputStr(void (*printChar)(char)) /** Reads replay from string using provided function that returns next character - in the string. Returns 1 on success, else 0. + in the string. The mapHash and nameHash pointers are optional: if non-zero, + they will be filled with the map hash and name hash. Returns 1 on success, + else 0. */ -int LCR_replayLoadFromStr(char (*nextChar)(void)) +int LCR_replayLoadFromStr(char (*nextChar)(void), + uint32_t *mapHash, uint16_t *nameHash) { char c = ' '; LCR_replay.eventCount = 0; LCR_replay.achievedTime = 0; +if (nameHash) + *nameHash = _LCR_simpleStrHash(nextChar,';'); +else + _LCR_simpleStrHash(nextChar,';'); + +/* do // map name { c = nextChar(); @@ -220,10 +227,27 @@ int LCR_replayLoadFromStr(char (*nextChar)(void)) if (c == 0) return 0; } while (c != ';'); +*/ +if (mapHash) + *mapHash = 0; + +for (int i = 0; i < 8; ++i) // hash +{ + c = nextChar(); + + if (_LCR_hexDigitVal(c) < 0) + return 0; + + if (mapHash) + *mapHash = ((*mapHash) << 4) | _LCR_hexDigitVal(c); +} + +/* for (int i = 0; i < 8; ++i) // hash if (_LCR_hexDigitVal(nextChar()) < 0) return 0; +*/ nextChar();