Resource -> data

This commit is contained in:
Miloslav Ciz 2025-01-20 19:58:56 +01:00
parent 17a371fdca
commit 02d23048dc
3 changed files with 85 additions and 86 deletions

View file

@ -40,7 +40,7 @@ static const char *LCR_texts[] =
"loading" "loading"
}; };
static const char *LCR_internalResourceFile = static const char *LCR_internalDataFile =
"Mtestmap;" "Mtestmap;"
"52123 1 :*H1k0" "52123 1 :*H1k0"

View file

@ -13,12 +13,12 @@ uint16_t screen[LCR_SETTING_RESOLUTION_X * LCR_SETTING_RESOLUTION_Y];
FILE *musicFile = 0; FILE *musicFile = 0;
char LCR_getNextResourceFileChar(void) char LCR_getNextDataFileChar(void)
{ {
return 0; return 0;
} }
void LCR_appendResourceStr(const char *str) void LCR_appendDataStr(const char *str)
{ {
return; return;
} }

165
game.h
View file

@ -27,22 +27,21 @@
their coordinate system (X right, Y up, Z forward) and rotations (Euler their coordinate system (X right, Y up, Z forward) and rotations (Euler
angles, by Z, then by X, then Y). angles, by Z, then by X, then Y).
RESOURCE FILE: The game uses so called resource file to store various RESOURCE FILE: The game uses so called data file to store various resources,
resources, mainly maps and replays. There is considered to be one abstract mainly maps and replays. There is considered to be one abstract global file
global file which is just a long text string. Internally the global file is which is just a long text string. Internally the global file is composed of
composed of a hardcoded internal resource file string (stored in assets) with a hardcoded internal data file string (stored in assets) with very basic maps,
very basic maps, and an optional user file (appended to the internal file) and an optional user file (appended to the internal file) that the frontend
that the frontend may provide, allowing adding more resources without may provide, allowing adding more resources without recompiling the game. The
recompiling the game. The user file may be disabled on platforms that e.g. user file may be disabled on platforms that e.g. don't have file systems, but
don't have file systems, but the internal file will be always present. The the internal file will be always present. The format of the data file is
format of the resource file is following: it consists of resource strings following: it consists of data strings separated by '#' character (data
separated by '#' character (resource strings cannot contain this character). strings cannot contain this character). Each data string starts with a magic
Each resource string starts with a magic number: a single character number: a single character identifying the type of the resource (map, replay,
identifying the type of the resource (map, replay, ...), then the name of the ...), then the name of the data follows, then character ';' and then the data
resource follows, then character ';' and then the resource string itself (up string itself (up until next '#' or end of file). The format of the string
until next '#' or end of file). The format of the string depends on the type depends on the type of the data, i.e. the format of map string has a special
of resource, i.e. the format of map string has a special format (described in format (described in the map module) etc.
the map module) etc.
*/ */
#define LCR_KEY_UP 0x00 #define LCR_KEY_UP 0x00
@ -98,23 +97,23 @@ void LCR_log(const char *str);
/** /**
Implement this in your frontend. This function serves for loading optional Implement this in your frontend. This function serves for loading optional
resource file that allows to add more maps, replays etc. If your frontend data file that allows to add more maps, replays etc. If your frontend
won't support this, just make the function return 0. Otherwise it must return won't support this, just make the function return 0. Otherwise it must return
characters from the resource file one by one; after reaching the end of file characters from the data file one by one; after reaching the end of file
0 must be returned and the reading position will be reset to start again. 0 must be returned and the reading position will be reset to start again.
*/ */
char LCR_getNextResourceFileChar(void); char LCR_getNextDataFileChar(void);
/** /**
Implement this in your frontend. This serves to store data in the optional Implement this in your frontend. This serves to store data in the optional
resource file, e.g. replays. If your frontend doesn't support this (e.g. data file, e.g. replays. If your frontend doesn't support this (e.g.
because the file is read only), the function may ignore the append, but if because the file is read only), the function may ignore the append, but if
the file is otherwise supported, a rewind of the read position must still be the file is otherwise supported, a rewind of the read position must still be
done. If appending is supported, the function must append the provided string done. If appending is supported, the function must append the provided string
to the resource file AND then reset the resource file reading position back to to the data file AND then reset the data file reading position back to
the start. the start.
*/ */
void LCR_appendResourceStr(const char *str); void LCR_appendDataStr(const char *str);
/** /**
Call this function in your frontend at the start of the program. Call this function in your frontend at the start of the program.
@ -259,10 +258,10 @@ struct
{ {
int state; ///< -1 if reading external res. f., else pos. int state; ///< -1 if reading external res. f., else pos.
// indices and counts are among the resources of the same type // indices and counts are among the data of the same type
unsigned int firstItemIndex; unsigned int firstItemIndex;
unsigned int itemsTotal; unsigned int itemsTotal;
} resourceFile; } dataFile;
} LCR_game; } LCR_game;
uint8_t LCR_gameMusicOn(void) uint8_t LCR_gameMusicOn(void)
@ -334,83 +333,82 @@ void LCR_gameResetRun(uint8_t replay)
} }
/** /**
Rewinds the global resource reading head to the beginning. Rewinds the global data file reading head to the beginning.
*/ */
void LCR_gameRewindResourceFile(void) void LCR_gameRewindDataFile(void)
{ {
LCR_appendResourceStr(""); LCR_appendDataStr("");
LCR_game.resourceFile.state = 0; LCR_game.dataFile.state = 0;
} }
/** /**
Reads the next global resource file character (merged internal resource file Reads the next global data file character (merged internal data file
with the optional user file). First the internal file will be read, with the optional user file). First the internal file will be read,
immediately followed by the user file, then zero char will be returned and immediately followed by the user file, then zero char will be returned and
then reading starts over. then reading starts over.
*/ */
char LCR_gameGetNextResourceFileChar(void) char LCR_gameGetNextDataFileChar(void)
{ {
#if LCR_SETTING_ENABLE_RESOURCE_FILE #if LCR_SETTING_ENABLE_RESOURCE_FILE
char c; char c;
if (LCR_game.resourceFile.state < 0) // external file? if (LCR_game.dataFile.state < 0) // external file?
{ {
c = LCR_getNextResourceFileChar(); c = LCR_getNextDataFileChar();
if (c == 0) if (c == 0)
LCR_game.resourceFile.state = 0; // move to internal file next LCR_game.dataFile.state = 0; // move to internal file next
} }
else // internal file else // internal file
{ {
c = LCR_internalResourceFile[LCR_game.resourceFile.state]; c = LCR_internalDataFile[LCR_game.dataFile.state];
LCR_game.resourceFile.state++; LCR_game.dataFile.state++;
if (c == 0) if (c == 0)
{ {
c = LCR_getNextResourceFileChar(); c = LCR_getNextDataFileChar();
LCR_game.resourceFile.state = c ? -1 : 0; // trust this LCR_game.dataFile.state = c ? -1 : 0; // trust this
} }
} }
return c; return c;
#else #else
if (LCR_internalResourceFile[LCR_game.resourceFile.state] == 0) if (LCR_internalDataFile[LCR_game.dataFile.state] == 0)
{ {
LCR_game.resourceFile.state = 0; LCR_game.dataFile.state = 0;
return 0; return 0;
} }
return LCR_internalResourceFile[LCR_game.resourceFile.state++]; return LCR_internalDataFile[LCR_game.dataFile.state++];
#endif #endif
} }
/** /**
Similar to LCR_gameGetNextResourceFileChar, but returns 0 instead of the Similar to LCR_gameGetNextDataFileChar, but returns 0 instead of the data
resource string separator character. This function is meant to be used by string separator character. This function is meant to be used by functions
functions that load something from a string while expecting a zero terminated that load something from a string while expecting a zero terminated string.
string.
*/ */
char LCR_gameGetNextResourceStrChar(void) char LCR_gameGetNextDataStrChar(void)
{ {
char c = LCR_gameGetNextResourceFileChar(); char c = LCR_gameGetNextDataFileChar();
return c != LCR_RESOURCE_FILE_SEPARATOR ? c : 0; return c != LCR_RESOURCE_FILE_SEPARATOR ? c : 0;
} }
/** /**
Seeks to the Nth resource string in the global resource file, after the magic Seeks to the Nth data string in the global data file, after the magic number,
number, so that the name is now available for reading. so that the name is now available for reading.
*/ */
void LCR_seekResourceByIndex(unsigned int index, char magicNumber) void LCR_seekDataByIndex(unsigned int index, char magicNumber)
{ {
char c; char c;
LCR_LOG0("seeking resource string"); LCR_LOG0("seeking data string");
LCR_gameRewindResourceFile(); LCR_gameRewindDataFile();
do do
{ {
c = LCR_gameGetNextResourceFileChar(); c = LCR_gameGetNextDataFileChar();
if (c == magicNumber) if (c == magicNumber)
{ {
@ -421,7 +419,7 @@ void LCR_seekResourceByIndex(unsigned int index, char magicNumber)
} }
while (c != 0 && c != LCR_RESOURCE_FILE_SEPARATOR) while (c != 0 && c != LCR_RESOURCE_FILE_SEPARATOR)
c = LCR_gameGetNextResourceFileChar(); c = LCR_gameGetNextDataFileChar();
} while (c); } while (c);
} }
@ -430,13 +428,13 @@ void LCR_gameLoadMap(unsigned int mapIndex)
{ {
char mapName[LCR_MAP_NAME_MAX_LEN]; char mapName[LCR_MAP_NAME_MAX_LEN];
LCR_seekResourceByIndex(mapIndex,'M'); LCR_seekDataByIndex(mapIndex,'M');
mapName[0] = 0; mapName[0] = 0;
for (int i = 0; i < LCR_MAP_NAME_MAX_LEN; ++i) for (int i = 0; i < LCR_MAP_NAME_MAX_LEN; ++i)
{ {
char c = LCR_gameGetNextResourceFileChar(); char c = LCR_gameGetNextDataFileChar();
if (c == LCR_RESOURCE_FILE_SEPARATOR2 || if (c == LCR_RESOURCE_FILE_SEPARATOR2 ||
c == LCR_RESOURCE_FILE_SEPARATOR || c == 0) c == LCR_RESOURCE_FILE_SEPARATOR || c == 0)
@ -446,7 +444,7 @@ void LCR_gameLoadMap(unsigned int mapIndex)
mapName[i + 1] = 0; mapName[i + 1] = 0;
} }
LCR_mapLoadFromStr(LCR_gameGetNextResourceStrChar,mapName); LCR_mapLoadFromStr(LCR_gameGetNextDataStrChar,mapName);
} }
/** /**
@ -463,21 +461,21 @@ unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
LCR_LOG1("loading replay and map"); LCR_LOG1("loading replay and map");
LCR_seekResourceByIndex(replayIndex,'R'); LCR_seekDataByIndex(replayIndex,'R');
do // skip name do // skip name
{ {
c = LCR_gameGetNextResourceFileChar(); c = LCR_gameGetNextDataFileChar();
} }
while (c && c != LCR_RESOURCE_FILE_SEPARATOR2 && while (c && c != LCR_RESOURCE_FILE_SEPARATOR2 &&
c != LCR_RESOURCE_FILE_SEPARATOR); c != LCR_RESOURCE_FILE_SEPARATOR);
if (!LCR_replayLoadFromStr(LCR_gameGetNextResourceStrChar, if (!LCR_replayLoadFromStr(LCR_gameGetNextDataStrChar,
&mapHash,&nameHash)) &mapHash,&nameHash))
return -2; return -2;
// now try to find the map with given nameHash // now try to find the map with given nameHash
LCR_gameRewindResourceFile(); LCR_gameRewindDataFile();
unsigned int skipTo = 0; unsigned int skipTo = 0;
@ -487,14 +485,14 @@ unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
while (1) // find first skipToth map while (1) // find first skipToth map
{ {
c = LCR_gameGetNextResourceFileChar(); c = LCR_gameGetNextDataFileChar();
if (c == 0) if (c == 0)
return -1; return -1;
else if (c == 'M') else if (c == 'M')
{ {
if (mapIndex >= skipTo && if (mapIndex >= skipTo &&
nameHash == _LCR_simpleStrHash(LCR_gameGetNextResourceStrChar,';')) nameHash == _LCR_simpleStrHash(LCR_gameGetNextDataStrChar,';'))
{ {
LCR_LOG2("map name hash matches"); LCR_LOG2("map name hash matches");
LCR_gameLoadMap(mapIndex); LCR_gameLoadMap(mapIndex);
@ -518,7 +516,7 @@ unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
if (c == 0) if (c == 0)
return -1; return -1;
c = LCR_gameGetNextResourceFileChar(); c = LCR_gameGetNextDataFileChar();
} }
} }
} }
@ -569,7 +567,7 @@ void LCR_gameInit(void)
LCR_racingInit(); LCR_racingInit();
LCR_audioInit(); LCR_audioInit();
LCR_game.resourceFile.state = 0; LCR_game.dataFile.state = 0;
for (int i = 0; i < LCR_MENU_MAX_ITEMS; ++i) for (int i = 0; i < LCR_MENU_MAX_ITEMS; ++i)
LCR_game.menu.itemNamePtrs[i] = LCR_game.menu.itemNames[i]; LCR_game.menu.itemNamePtrs[i] = LCR_game.menu.itemNames[i];
@ -594,17 +592,17 @@ void LCR_gameInit(void)
Loads up to LCR_RESOURCE_ITEM_CHUNK items of given type, starting at given Loads up to LCR_RESOURCE_ITEM_CHUNK items of given type, starting at given
index (among items of the same type). This will also load the menu item names. index (among items of the same type). This will also load the menu item names.
*/ */
void LCR_gameLoadResourceFileChunk(unsigned int startIndex, char magicNumber) void LCR_gameLoadDataFileChunk(unsigned int startIndex, char magicNumber)
{ {
char c; char c;
unsigned char state = 0; unsigned char state = 0;
LCR_gameEraseMenuItemNames(); LCR_gameEraseMenuItemNames();
LCR_game.resourceFile.firstItemIndex = startIndex; LCR_game.dataFile.firstItemIndex = startIndex;
LCR_game.resourceFile.itemsTotal = 0; LCR_game.dataFile.itemsTotal = 0;
LCR_gameRewindResourceFile(); LCR_gameRewindDataFile();
/* 3 iterations: in first we seek to the start index, in second we load the /* 3 iterations: in first we seek to the start index, in second we load the
names, in third we just read the rest to get the total count. */ names, in third we just read the rest to get the total count. */
@ -615,7 +613,7 @@ void LCR_gameLoadResourceFileChunk(unsigned int startIndex, char magicNumber)
if (i == 0 && !startIndex) if (i == 0 && !startIndex)
break; break;
c = LCR_gameGetNextResourceFileChar(); c = LCR_gameGetNextDataFileChar();
if (c == 0) if (c == 0)
return; return;
@ -626,7 +624,7 @@ void LCR_gameLoadResourceFileChunk(unsigned int startIndex, char magicNumber)
if (c == magicNumber) if (c == magicNumber)
{ {
LCR_game.resourceFile.itemsTotal++; LCR_game.dataFile.itemsTotal++;
if (i == 0) if (i == 0)
startIndex--; startIndex--;
@ -777,7 +775,7 @@ void LCR_gameDraw3DView(void)
} }
} }
void _LCR_gameResourceCharWrite(char c) void _LCR_gameDataCharWrite(char c)
{ // TODO { // TODO
printf("%c",c); printf("%c",c);
} }
@ -822,7 +820,7 @@ void LCR_gameHandleInput(void)
LCR_audioPlaySound(LCR_SOUND_CLICK); LCR_audioPlaySound(LCR_SOUND_CLICK);
} }
else if (LCR_game.menu.selectedTab != 0 && else if (LCR_game.menu.selectedTab != 0 &&
LCR_game.resourceFile.firstItemIndex != 0) LCR_game.dataFile.firstItemIndex != 0)
{ {
LCR_game.menu.selectedItem = LCR_RESOURCE_ITEM_CHUNK - 1; LCR_game.menu.selectedItem = LCR_RESOURCE_ITEM_CHUNK - 1;
LCR_audioPlaySound(LCR_SOUND_CLICK); LCR_audioPlaySound(LCR_SOUND_CLICK);
@ -846,8 +844,8 @@ void LCR_gameHandleInput(void)
LCR_game.menu.selectedItem++; LCR_game.menu.selectedItem++;
LCR_audioPlaySound(LCR_SOUND_CLICK); LCR_audioPlaySound(LCR_SOUND_CLICK);
} }
else if (LCR_game.resourceFile.firstItemIndex + else if (LCR_game.dataFile.firstItemIndex +
LCR_RESOURCE_ITEM_CHUNK < LCR_game.resourceFile.itemsTotal) LCR_RESOURCE_ITEM_CHUNK < LCR_game.dataFile.itemsTotal)
{ {
LCR_game.menu.selectedItem = 0; LCR_game.menu.selectedItem = 0;
LCR_audioPlaySound(LCR_SOUND_CLICK); LCR_audioPlaySound(LCR_SOUND_CLICK);
@ -895,7 +893,7 @@ void LCR_gameHandleInput(void)
break; break;
case 1: case 1:
LCR_gameLoadMap(LCR_game.resourceFile.firstItemIndex + LCR_gameLoadMap(LCR_game.dataFile.firstItemIndex +
LCR_game.menu.selectedItem); LCR_game.menu.selectedItem);
LCR_gameSetState(LCR_GAME_STATE_LOADING_RUN); LCR_gameSetState(LCR_GAME_STATE_LOADING_RUN);
break; break;
@ -903,7 +901,7 @@ void LCR_gameHandleInput(void)
case 2: case 2:
case 3: case 3:
{ {
int mapIndex = LCR_gameLoadReplay(LCR_game.resourceFile.firstItemIndex + int mapIndex = LCR_gameLoadReplay(LCR_game.dataFile.firstItemIndex +
LCR_game.menu.selectedItem); LCR_game.menu.selectedItem);
if (mapIndex < -1) if (mapIndex < -1)
@ -915,7 +913,8 @@ void LCR_gameHandleInput(void)
LCR_LOG1("couldn't load replay map"); LCR_LOG1("couldn't load replay map");
} }
else else
LCR_gameSetState(LCR_GAME_STATE_LOADING_REP1); LCR_gameSetState(LCR_game.menu.selectedTab == 2 ?
LCR_GAME_STATE_LOADING_REP1 : LCR_GAME_STATE_LOADING_REP2);
break; break;
} }
@ -928,8 +927,7 @@ void LCR_gameHandleInput(void)
case LCR_GAME_STATE_RUN_FINISHED: case LCR_GAME_STATE_RUN_FINISHED:
if (LCR_game.keyStates[LCR_KEY_A] == 1) if (LCR_game.keyStates[LCR_KEY_A] == 1)
// LCR_gameResetRun(LCR_racing.playingReplay); LCR_gameResetRun(LCR_racing.playingReplay);
LCR_gameResetRun(1);
break; break;
@ -989,8 +987,8 @@ void LCR_gameHandleInput(void)
if (tabSwitchedTo == 0) if (tabSwitchedTo == 0)
LCR_gameLoadMainMenuItems(); LCR_gameLoadMainMenuItems();
else if (tabSwitchedTo > 0 || scrolled != 0) else if (tabSwitchedTo > 0 || scrolled != 0)
LCR_gameLoadResourceFileChunk( LCR_gameLoadDataFileChunk(
(tabSwitchedTo > 0) ? 0 : (LCR_game.resourceFile.firstItemIndex + (tabSwitchedTo > 0) ? 0 : (LCR_game.dataFile.firstItemIndex +
scrolled * LCR_RESOURCE_ITEM_CHUNK), scrolled * LCR_RESOURCE_ITEM_CHUNK),
LCR_game.menu.selectedTab == 1 ? 'M' : 'R'); LCR_game.menu.selectedTab == 1 ? 'M' : 'R');
} }
@ -1012,7 +1010,8 @@ uint8_t LCR_gameStep(uint32_t time)
if (_LCR_gameIsLoading()) if (_LCR_gameIsLoading())
{ {
LCR_rendererLoadMap(); LCR_rendererLoadMap();
LCR_gameResetRun(LCR_racing.playingReplay); LCR_gameResetRun(
LCR_game.state == LCR_GAME_STATE_LOADING_REP1);
} }
LCR_gameHandleInput(); LCR_gameHandleInput();
@ -1045,7 +1044,7 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_game.state != LCR_GAME_STATE_RUN_FINISHED) LCR_game.state != LCR_GAME_STATE_RUN_FINISHED)
{ {
LCR_LOG1("finished"); LCR_LOG1("finished");
LCR_replayOutputStr(_LCR_gameResourceCharWrite); LCR_replayOutputStr(_LCR_gameDataCharWrite);
LCR_audioPlaySound(LCR_SOUND_CLICK); LCR_audioPlaySound(LCR_SOUND_CLICK);
LCR_gameSetState(LCR_GAME_STATE_RUN_FINISHED); LCR_gameSetState(LCR_GAME_STATE_RUN_FINISHED);