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"
};
static const char *LCR_internalResourceFile =
static const char *LCR_internalDataFile =
"Mtestmap;"
"52123 1 :*H1k0"

View file

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

165
game.h
View file

@ -27,22 +27,21 @@
their coordinate system (X right, Y up, Z forward) and rotations (Euler
angles, by Z, then by X, then Y).
RESOURCE FILE: The game uses so called resource file to store various
resources, mainly maps and replays. There is considered to be one abstract
global file which is just a long text string. Internally the global file is
composed of a hardcoded internal resource file string (stored in assets) with
very basic maps, and an optional user file (appended to the internal file)
that the frontend may provide, allowing adding more resources without
recompiling the game. The user file may be disabled on platforms that e.g.
don't have file systems, but the internal file will be always present. The
format of the resource file is following: it consists of resource strings
separated by '#' character (resource strings cannot contain this character).
Each resource string starts with a magic number: a single character
identifying the type of the resource (map, replay, ...), then the name of the
resource follows, then character ';' and then the resource string itself (up
until next '#' or end of file). The format of the string depends on the type
of resource, i.e. the format of map string has a special format (described in
the map module) etc.
RESOURCE FILE: The game uses so called data file to store various resources,
mainly maps and replays. There is considered to be one abstract global file
which is just a long text string. Internally the global file is composed of
a hardcoded internal data file string (stored in assets) with very basic maps,
and an optional user file (appended to the internal file) that the frontend
may provide, allowing adding more resources without recompiling the game. The
user file may be disabled on platforms that e.g. don't have file systems, but
the internal file will be always present. The format of the data file is
following: it consists of data strings separated by '#' character (data
strings cannot contain this character). Each data string starts with a magic
number: a single character identifying the type of the resource (map, replay,
...), then the name of the data follows, then character ';' and then the data
string itself (up until next '#' or end of file). The format of the string
depends on the type of the data, i.e. the format of map string has a special
format (described in the map module) etc.
*/
#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
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
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.
*/
char LCR_getNextResourceFileChar(void);
char LCR_getNextDataFileChar(void);
/**
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
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
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.
*/
void LCR_appendResourceStr(const char *str);
void LCR_appendDataStr(const char *str);
/**
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.
// 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 itemsTotal;
} resourceFile;
} dataFile;
} LCR_game;
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_game.resourceFile.state = 0;
LCR_appendDataStr("");
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,
immediately followed by the user file, then zero char will be returned and
then reading starts over.
*/
char LCR_gameGetNextResourceFileChar(void)
char LCR_gameGetNextDataFileChar(void)
{
#if LCR_SETTING_ENABLE_RESOURCE_FILE
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)
LCR_game.resourceFile.state = 0; // move to internal file next
LCR_game.dataFile.state = 0; // move to internal file next
}
else // internal file
{
c = LCR_internalResourceFile[LCR_game.resourceFile.state];
LCR_game.resourceFile.state++;
c = LCR_internalDataFile[LCR_game.dataFile.state];
LCR_game.dataFile.state++;
if (c == 0)
{
c = LCR_getNextResourceFileChar();
LCR_game.resourceFile.state = c ? -1 : 0; // trust this
c = LCR_getNextDataFileChar();
LCR_game.dataFile.state = c ? -1 : 0; // trust this
}
}
return c;
#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 LCR_internalResourceFile[LCR_game.resourceFile.state++];
return LCR_internalDataFile[LCR_game.dataFile.state++];
#endif
}
/**
Similar to LCR_gameGetNextResourceFileChar, but returns 0 instead of the
resource string separator character. This function is meant to be used by
functions that load something from a string while expecting a zero terminated
string.
Similar to LCR_gameGetNextDataFileChar, but returns 0 instead of the data
string separator character. This function is meant to be used by functions
that load something from a string while expecting a zero terminated 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;
}
/**
Seeks to the Nth resource string in the global resource file, after the magic
number, so that the name is now available for reading.
Seeks to the Nth data string in the global data file, after the magic number,
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;
LCR_LOG0("seeking resource string");
LCR_LOG0("seeking data string");
LCR_gameRewindResourceFile();
LCR_gameRewindDataFile();
do
{
c = LCR_gameGetNextResourceFileChar();
c = LCR_gameGetNextDataFileChar();
if (c == magicNumber)
{
@ -421,7 +419,7 @@ void LCR_seekResourceByIndex(unsigned int index, char magicNumber)
}
while (c != 0 && c != LCR_RESOURCE_FILE_SEPARATOR)
c = LCR_gameGetNextResourceFileChar();
c = LCR_gameGetNextDataFileChar();
} while (c);
}
@ -430,13 +428,13 @@ void LCR_gameLoadMap(unsigned int mapIndex)
{
char mapName[LCR_MAP_NAME_MAX_LEN];
LCR_seekResourceByIndex(mapIndex,'M');
LCR_seekDataByIndex(mapIndex,'M');
mapName[0] = 0;
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 ||
c == LCR_RESOURCE_FILE_SEPARATOR || c == 0)
@ -446,7 +444,7 @@ void LCR_gameLoadMap(unsigned int mapIndex)
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_seekResourceByIndex(replayIndex,'R');
LCR_seekDataByIndex(replayIndex,'R');
do // skip name
{
c = LCR_gameGetNextResourceFileChar();
c = LCR_gameGetNextDataFileChar();
}
while (c && c != LCR_RESOURCE_FILE_SEPARATOR2 &&
c != LCR_RESOURCE_FILE_SEPARATOR);
if (!LCR_replayLoadFromStr(LCR_gameGetNextResourceStrChar,
if (!LCR_replayLoadFromStr(LCR_gameGetNextDataStrChar,
&mapHash,&nameHash))
return -2;
// now try to find the map with given nameHash
LCR_gameRewindResourceFile();
LCR_gameRewindDataFile();
unsigned int skipTo = 0;
@ -487,14 +485,14 @@ unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
while (1) // find first skipToth map
{
c = LCR_gameGetNextResourceFileChar();
c = LCR_gameGetNextDataFileChar();
if (c == 0)
return -1;
else if (c == 'M')
{
if (mapIndex >= skipTo &&
nameHash == _LCR_simpleStrHash(LCR_gameGetNextResourceStrChar,';'))
nameHash == _LCR_simpleStrHash(LCR_gameGetNextDataStrChar,';'))
{
LCR_LOG2("map name hash matches");
LCR_gameLoadMap(mapIndex);
@ -518,7 +516,7 @@ unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
if (c == 0)
return -1;
c = LCR_gameGetNextResourceFileChar();
c = LCR_gameGetNextDataFileChar();
}
}
}
@ -569,7 +567,7 @@ void LCR_gameInit(void)
LCR_racingInit();
LCR_audioInit();
LCR_game.resourceFile.state = 0;
LCR_game.dataFile.state = 0;
for (int i = 0; i < LCR_MENU_MAX_ITEMS; ++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
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;
unsigned char state = 0;
LCR_gameEraseMenuItemNames();
LCR_game.resourceFile.firstItemIndex = startIndex;
LCR_game.resourceFile.itemsTotal = 0;
LCR_game.dataFile.firstItemIndex = startIndex;
LCR_game.dataFile.itemsTotal = 0;
LCR_gameRewindResourceFile();
LCR_gameRewindDataFile();
/* 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. */
@ -615,7 +613,7 @@ void LCR_gameLoadResourceFileChunk(unsigned int startIndex, char magicNumber)
if (i == 0 && !startIndex)
break;
c = LCR_gameGetNextResourceFileChar();
c = LCR_gameGetNextDataFileChar();
if (c == 0)
return;
@ -626,7 +624,7 @@ void LCR_gameLoadResourceFileChunk(unsigned int startIndex, char magicNumber)
if (c == magicNumber)
{
LCR_game.resourceFile.itemsTotal++;
LCR_game.dataFile.itemsTotal++;
if (i == 0)
startIndex--;
@ -777,7 +775,7 @@ void LCR_gameDraw3DView(void)
}
}
void _LCR_gameResourceCharWrite(char c)
void _LCR_gameDataCharWrite(char c)
{ // TODO
printf("%c",c);
}
@ -822,7 +820,7 @@ void LCR_gameHandleInput(void)
LCR_audioPlaySound(LCR_SOUND_CLICK);
}
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_audioPlaySound(LCR_SOUND_CLICK);
@ -846,8 +844,8 @@ void LCR_gameHandleInput(void)
LCR_game.menu.selectedItem++;
LCR_audioPlaySound(LCR_SOUND_CLICK);
}
else if (LCR_game.resourceFile.firstItemIndex +
LCR_RESOURCE_ITEM_CHUNK < LCR_game.resourceFile.itemsTotal)
else if (LCR_game.dataFile.firstItemIndex +
LCR_RESOURCE_ITEM_CHUNK < LCR_game.dataFile.itemsTotal)
{
LCR_game.menu.selectedItem = 0;
LCR_audioPlaySound(LCR_SOUND_CLICK);
@ -895,7 +893,7 @@ void LCR_gameHandleInput(void)
break;
case 1:
LCR_gameLoadMap(LCR_game.resourceFile.firstItemIndex +
LCR_gameLoadMap(LCR_game.dataFile.firstItemIndex +
LCR_game.menu.selectedItem);
LCR_gameSetState(LCR_GAME_STATE_LOADING_RUN);
break;
@ -903,7 +901,7 @@ void LCR_gameHandleInput(void)
case 2:
case 3:
{
int mapIndex = LCR_gameLoadReplay(LCR_game.resourceFile.firstItemIndex +
int mapIndex = LCR_gameLoadReplay(LCR_game.dataFile.firstItemIndex +
LCR_game.menu.selectedItem);
if (mapIndex < -1)
@ -915,7 +913,8 @@ void LCR_gameHandleInput(void)
LCR_LOG1("couldn't load replay map");
}
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;
}
@ -928,8 +927,7 @@ void LCR_gameHandleInput(void)
case LCR_GAME_STATE_RUN_FINISHED:
if (LCR_game.keyStates[LCR_KEY_A] == 1)
// LCR_gameResetRun(LCR_racing.playingReplay);
LCR_gameResetRun(1);
LCR_gameResetRun(LCR_racing.playingReplay);
break;
@ -989,8 +987,8 @@ void LCR_gameHandleInput(void)
if (tabSwitchedTo == 0)
LCR_gameLoadMainMenuItems();
else if (tabSwitchedTo > 0 || scrolled != 0)
LCR_gameLoadResourceFileChunk(
(tabSwitchedTo > 0) ? 0 : (LCR_game.resourceFile.firstItemIndex +
LCR_gameLoadDataFileChunk(
(tabSwitchedTo > 0) ? 0 : (LCR_game.dataFile.firstItemIndex +
scrolled * LCR_RESOURCE_ITEM_CHUNK),
LCR_game.menu.selectedTab == 1 ? 'M' : 'R');
}
@ -1012,7 +1010,8 @@ uint8_t LCR_gameStep(uint32_t time)
if (_LCR_gameIsLoading())
{
LCR_rendererLoadMap();
LCR_gameResetRun(LCR_racing.playingReplay);
LCR_gameResetRun(
LCR_game.state == LCR_GAME_STATE_LOADING_REP1);
}
LCR_gameHandleInput();
@ -1045,7 +1044,7 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_game.state != LCR_GAME_STATE_RUN_FINISHED)
{
LCR_LOG1("finished");
LCR_replayOutputStr(_LCR_gameResourceCharWrite);
LCR_replayOutputStr(_LCR_gameDataCharWrite);
LCR_audioPlaySound(LCR_SOUND_CLICK);
LCR_gameSetState(LCR_GAME_STATE_RUN_FINISHED);