/* y_bin - Binary file storage. */ /* Binary file format: 4 bytes - Number of chunks in the file. Chunk header: These are not garbage collected, and memory is allocated for a huge number. Actually, I'm not sure of the best way to do this well... The original idea was to just have fixed blocks, I don't know if that is still the best method. Actually, maybe the best method is separate files created and destroyed as required, that would be a LOT less coding! */ stock INI_WriteArray(const INI:f, const string:name[], const arr[], const len = sizeof (arr)) { // First things first, determine the filename for the data. This is done as: // // \.INI[]= // // Also, unlike all other INI data, this data is not buffered - doing so is // not only hard, but pointless because we are writing to a different file. new fname[64]; format(fname, sizeof (fname), "%s[%s]%s=.bin", YSI_g_sINIWriteFile[f], YSI_g_sINITagBuffer[YSI_g_sINICurrentTag[f]][E_INI_TAGS_NAME], name); new File:b = fopen(fname, io_write); if (b) { fblockwrite(b, arr, len); INI_WriteString(f, name, fname), fclose(b); } } stock INI_ReadArray(const string:fname[], dest[], len = sizeof (dest)) { new File:b = fopen(fname, io_read); if (b) { new l2 = flength(b); if (l2 > len) P:W("%s buffer has shrunk since writing.", fname); else if (l2 < len) P:W("%s buffer has grown since writing.", fname); return fblockread(b, arr, len); fclose(b), 1; } else { // Do something special to detect old style arrays. Sadly, we can't // upgrade them as they may not be writing (actually, why can't we?) } return 0; } #define INI_Array(%1,%2) \ if (!strcmp((%1), name, true)) return INI_ReadArray(value, %2) /* BIN_WriteChunk(const BIN:f, const pos, const arr[], const len = sizeof (arr), buffer = sizeof (arr)) { if (buffer < len) { // Blank the file location, return the new location. Can NEVER garbage // collect the wasted storage. Actually, why can't we? Use double // indirection! } else if (buffer > len) { // Pad the data. } } */ /**--------------------------------------------------------------------------**\ INI_TryGetValue Start position of the possibly current key. End position of the found key. - The INI system uses basic string functions to find candidate keys. They always start searching from the known start of the current tag, and check that the found key is before the start of the next known tag. If both those conditions are true, this function is called to further verify the found text - it could be a substring in a larger key, it could be a value, or it could be commented out. This function therefore checks that the found position in the stored string is the first item on a line, and is followed only by either nothing or an equals sign. If this IS the key we are searching for, then we return a later position corresponding to the start of the value (or cellmax if there is no value). If this isn't a valid key, we return -1. \**--------------------------------------------------------------------------**/ static stock bool:INI_TryGetValue(start, end) { // Check everything before this position is whitespace only. new cur = start; for ( ; ; ) { switch (YSI_gMallocMemory[--cur]) { case '\0', '\r', '\n': break; case ' ', '\t': {} default: return -1; } } cur = end; for ( ; ; ) { switch (YSI_gMallocMemory[cur++]) { // Has no value, just a key. case '\0', '\r', '\n': return cellmax; // Has a value, find the start. case '=': { // "cur" has already been incremented by this point. for ( ; ; ) { switch (YSI_gMallocMemory[cur]) { case '\0', '\r', '\n': return cellmax; case ' ', '\t': ++cur; default: return cur; } } } case ' ', '\t': {} default: break; } } return -1; }