/*
Legal:
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 the "License"; you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the YSI framework.
The Initial Developer of the Original Code is Alex "Y_Less" Cole.
Portions created by the Initial Developer are Copyright C 2011
the Initial Developer. All Rights Reserved.
Contributors:
Y_Less
koolk
JoeBullet/Google63
g_aSlice/Slice
Misiur
samphunter
tianmeta
maddinat0r
spacemud
Crayder
Dayvison
Ahmad45123
Zeex
irinel1996
Yiin-
Chaprnks
Konstantinos
Masterchen09
Southclaws
PatchwerkQWER
m0k1
paulommu
udan111
Thanks:
JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
ZeeX - Very productive conversations.
koolk - IsPlayerinAreaEx code.
TheAlpha - Danish translation.
breadfish - German translation.
Fireburn - Dutch translation.
yom - French translation.
50p - Polish translation.
Zamaroht - Spanish translation.
Los - Portuguese translation.
Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
me to strive to better.
Pixels^ - Running XScripters where the idea was born.
Matite - Pestering me to release it and using it.
Very special thanks to:
Thiadmer - PAWN, whose limits continue to amaze me!
Kye/Kalcor - SA:MP.
SA:MP Team past, present and future - SA:MP.
Optional plugins:
Gamer_Z - GPS.
Incognito - Streamer.
Me - sscanf2, fixes2, Whirlpool.
*/
/*
ad88888ba
d8" "8b ,d
Y8, 88
`Y8aaaaa, ,adPPYba, MM88MMM 88 88 8b,dPPYba,
`"""""8b, a8P_____88 88 88 88 88P' "8a
`8b 8PP""""""" 88 88 88 88 d8
Y8a a8P "8b, ,aa 88, "8a, ,a88 88b, ,a8"
"Y88888P" `"Ybbd8"' "Y888 `"YbbdP'Y8 88`YbbdP"'
88
88
*/
enum E_INI_KV_ENTRY
{
E_INI_KV_ENTRY_NAME[MAX_INI_ENTRY_NAME],
E_INI_KV_ENTRY_TEXT[MAX_INI_ENTRY_TEXT],
E_INI_KV_ENTRY_NEXT
}
enum E_INI_TAGS
{
E_INI_TAGS_NAME[MAX_INI_TAG char],
E_INI_TAGS_START,
E_INI_TAGS_NEXT
}
_Y_INI_STATIC stock
YSI_g_sINITmpBuffer[36],
YSI_g_sINIWriteBuffer[INI_MAX_WRITES * INI_BUFFER_SIZE][E_INI_KV_ENTRY],
YSI_g_sINIWritePos, // Pointer to the first free K/V slot.
YSI_g_sINITagPos, // Pointer to the first free tag slot.
YSI_g_sINITagBuffer[INI_MAX_WRITES * MAX_INI_TAGS][E_INI_TAGS],
YSI_g_sINICurrentTag[INI:INI_MAX_WRITES],
YSI_g_sINIStartTag[INI:INI_MAX_WRITES],
YSI_g_sINIWriteFile[INI:INI_MAX_WRITES][YSI_MAX_STRING];
forward bool:INI_WriteBuffer(INI:file);
forward bool:INI_Flush();
/*
88b d88
888b d888
88`8b d8'88
88 `8b d8' 88 ,adPPYYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba,
88 `8b d8' 88 "" `Y8 a8" "" 88P' "Y8 a8" "8a I8[ ""
88 `8b d8' 88 ,adPPPPP88 8b 88 8b d8 `"Y8ba,
88 `888' 88 88, ,88 "8a, ,aa 88 "8a, ,a8" aa ]8I
88 `8' 88 `"8bbdP"Y8 `"Ybbd8"' 88 `"YbbdP"' `"YbbdP"'
*/
#define INI_IsValid(%0) (0 <= _:(%0) < INI_MAX_WRITES && YSI_g_sINIWriteFile[(%0)][0])
#define INI_WriteComments() \
fwrite(buffer, ";"), \
fwrite(buffer, sLine[p2s])
#define INI_IsDeleted(%0) ((%0) != -1 && YSI_g_sINITagBuffer[(%0)][E_INI_TAGS_START] == cellmax)
/*
88b d88 88 db 88888888ba 88
888b d888 "" d88b 88 "8b 88
88`8b d8'88 d8'`8b 88 ,8P 88
88 `8b d8' 88 ,adPPYYba, 88 8b,dPPYba, d8' `8b 88aaaaaa8P' 88
88 `8b d8' 88 "" `Y8 88 88P' `"8a d8YaaaaY8b 88""""""' 88
88 `8b d8' 88 ,adPPPPP88 88 88 88 d8""""""""8b 88 88
88 `888' 88 88, ,88 88 88 88 d8' `8b 88 88
88 `8' 88 `"8bbdP"Y8 88 88 88 d8' `8b 88 88
*/
/*-------------------------------------------------------------------------*//**
* INI file to open.
*
* INI - handle to the file or INI_NO_FILE.
*
*
* Doesn't actually open the file, just starts a new buffer if possible.
*
*//*------------------------------------------------------------------------**/
stock INI:INI_Open(const filename[])
{
if (ftouch(filename) == -1)
{
P:W("INI_Open could not find or create file %s", filename);
}
P:3("INI:INI_Open called: \"%s\"", filename);
new
i;
while (i != INI_MAX_WRITES && YSI_g_sINIWriteFile[INI:i][0]) ++i;
if (i == INI_MAX_WRITES) return INI_NO_FILE;
return
strcpy(YSI_g_sINIWriteFile[INI:i], filename, YSI_MAX_STRING),
// Reset tags.
YSI_g_sINIStartTag[INI:i] = -1,
YSI_g_sINICurrentTag[INI:i] = -1,
INI:i;
}
/*-------------------------------------------------------------------------*//**
* Handle to the ini to close.
*
* Writes any outstanding buffer data to the file and ends the stream.
*
*//*------------------------------------------------------------------------**/
stock INI_Close(INI:file)
{
P:3("INI_Close called: %i", _:file);
if (INI_IsValid(file))
{
INI_WriteBuffer(file), // Used to check, now just flush by default.
YSI_g_sINIWriteFile[file][0] = '\0';
}
}
/*-------------------------------------------------------------------------*//**
* INI file handle to write to.
* Name of the new file subsection for subsequent data to write to.
*
* Sets a new [tag] section header. Subsequent data is written under this
* header. Uses lists for constant tag switching and checks the tag doesn't
* already exist.
*
*//*------------------------------------------------------------------------**/
stock INI_SetTag(INI:file, const tag[])
{
P:3("INI_SetTag called: %i, \"%s\"", _:file, tag);
if (INI_IsValid(file))
{
// Loop through the tags for this file.
new
cur = INI_GetTag(file, tag);
if (cur != -1) return (YSI_g_sINICurrentTag[file] = cur);
// May need some extra space.
if (YSI_g_sINITagPos == -1 && !INI_Flush()) return -1;
// Tag hasn't been written to yet this flush.
return
// Get a new tag.
cur = YSI_g_sINITagPos,
// Update the "free" list.
YSI_g_sINITagPos = YSI_g_sINITagBuffer[cur][E_INI_TAGS_NEXT],
// Add this tag to the current file's list.
YSI_g_sINITagBuffer[cur][E_INI_TAGS_NEXT] = YSI_g_sINIStartTag[file],
YSI_g_sINIStartTag[file] = cur,
// Copy the name over.
strpack(YSI_g_sINITagBuffer[cur][E_INI_TAGS_NAME], tag, MAX_INI_TAG char),
// Set start points.
YSI_g_sINITagBuffer[cur][E_INI_TAGS_START] = -1,
YSI_g_sINICurrentTag[file] = cur;
}
return -1;
}
/*-------------------------------------------------------------------------*//**
* INI file handle to write to.
* Name of the whole section to delete.
*
* Removes a [tag] section from a file.
*
*//*------------------------------------------------------------------------**/
stock INI_DeleteTag(INI:file, const tag[])
{
P:3("INI_DeleteTag called: %i, \"%s\"", _:file, tag);
if (INI_IsValid(file))
{
// Save the tag currently being written to.
new
curTag = YSI_g_sINICurrentTag[file],
tag2[MAX_INI_TAG];
if (curTag != -1) strunpack(tag2, YSI_g_sINITagBuffer[curTag][E_INI_TAGS_NAME]);
new
// Get a pointer to the new tag.
tp = INI_SetTag(file, tag),
cur = YSI_g_sINITagBuffer[tp][E_INI_TAGS_START];
// If there is anything in the tag, discard it. Accounts for "cellmax"
// specials.
while (0 <= cur < sizeof (YSI_g_sINIWriteBuffer)) cur = INI_FreeEntry(cur);
// Now set it to the special "deleted" mark.
YSI_g_sINITagBuffer[tp][E_INI_TAGS_START] = cellmax;
// Reset the current tag, using a name not a pointer - we may have
// flushed the buffer in the meantime and thus invalidated "curTag".
YSI_g_sINICurrentTag[file] = (curTag == -1) ? -1 : INI_GetTag(file, tag2);
}
}
/*-------------------------------------------------------------------------*//**
* File to write to.
* Item to remove.
*
* Wrapper for INI_AddToBuffer for removing data.
*
*//*------------------------------------------------------------------------**/
stock INI_RemoveEntry(INI:file, name[])
{
P:3("INI_RemoveEntry called: %i, \"%s\"", _:file, name);
return INI_AddToBuffer(file, name, NULL);
}
/*
I8, 8 ,8I 88 88
`8b d8b d8' "" ,d ""
"8, ,8"8, ,8" 88
Y8 8P Y8 8P 8b,dPPYba, 88 MM88MMM 88 8b,dPPYba, ,adPPYb,d8
`8b d8' `8b d8' 88P' "Y8 88 88 88 88P' `"8a a8" `Y88
`8a a8' `8a a8' 88 88 88 88 88 88 8b 88
`8a8' `8a8' 88 88 88, 88 88 88 "8a, ,d88
`8' `8' 88 88 "Y888 88 88 88 `"YbbdP"Y8
aa, ,88
"Y8bbdP"
*/
/*-------------------------------------------------------------------------*//**
* File to write to.
* Data name.
* Data.
*
* Wrapper for INI_AddToBuffer for strings.
*
*//*------------------------------------------------------------------------**/
stock INI_WriteString(INI:file, name[], data[])
{
P:3("INI_WriteString called: %i, \"%s\", \"%s\"", _:file, name, data);
return INI_AddToBuffer(file, name, data);
}
/*-------------------------------------------------------------------------*//**
* File to write to.
* Data name.
* Integer data.
*
* Wrapper for INI_AddToBuffer for integers. Fixed for very large numbers
* based on code by Slice from "fixes.inc" for "valstr".
*
*//*------------------------------------------------------------------------**/
stock INI_WriteInt(INI:file, name[], data)
{
P:3("INI_WriteInt called: %i, \"%s\", %i", _:file, name, data);
static const
sc_szCellmin[] = !"-2147483648";
if (data == cellmin)
{
return INI_AddToBuffer(file, name, sc_szCellmin);
}
else
{
return
format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "%d", data),
INI_AddToBuffer(file, name, YSI_g_sINITmpBuffer);
}
}
/*-------------------------------------------------------------------------*//**
* File to write to.
* Data name.
* Hex data.
*
* Wrapper for INI_AddToBuffer for integers to be written as hex values.
*
*//*------------------------------------------------------------------------**/
stock INI_WriteHex(INI:file, name[], data)
{
P:3("INI_WriteHex called: %i, \"%s\", %i", _:file, name, data);
return
format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "0x%04x%04x", data >>> 16, data & 0xFFFF),
INI_AddToBuffer(file, name, YSI_g_sINITmpBuffer);
}
/*-------------------------------------------------------------------------*//**
* File to write to.
* Data name.
* Binary data.
*
* Wrapper for INI_AddToBuffer for integers to be written as binary values.
*
*//*------------------------------------------------------------------------**/
stock INI_WriteBin(INI:file, name[], data)
{
P:3("INI_WriteBin called: %i, \"%s\", %i", _:file, name, data);
if (data < 0) format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "0b1%031b", data & 0x7FFFFFFF);
else format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "0b%b", data);
return INI_AddToBuffer(file, name, YSI_g_sINITmpBuffer);
}
/*-------------------------------------------------------------------------*//**
* File to write to.
* Data name.
* Boolean data.
*
* Wrapper for INI_AddToBuffer for booleans.
*
*//*------------------------------------------------------------------------**/
stock INI_WriteBool(INI:file, name[], bool:data)
{
P:3("INI_WriteBool called: %i, \"%s\", %i", _:file, name, _:data);
return INI_AddToBuffer(file, name, data ? ("true") : ("false"));
}
/*-------------------------------------------------------------------------*//**
* File to write to.
* Data name.
* Float data.
* number of decimal places to write.
*
* Wrapper for INI_AddToBuffer for floats.
*
*//*------------------------------------------------------------------------**/
stock INI_WriteFloat(INI:file, name[], Float:data, accuracy = 6)
{
P:3("INI_WriteFloat called: %i, \"%s\", %f, %i", _:file, name, data, accuracy);
return
format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "%.*f", accuracy, data),
INI_AddToBuffer(file, name, YSI_g_sINITmpBuffer);
}
/*
88 88 88
88 88 88
88 88 88
88aaaaaaaa88 ,adPPYba, ,adPPYba, 88 ,d8 ,adPPYba,
88""""""""88 a8" "8a a8" "8a 88 ,a8" I8[ ""
88 88 8b d8 8b d8 8888[ `"Y8ba,
88 88 "8a, ,a8" "8a, ,a8" 88`"Yba, aa ]8I
88 88 `"YbbdP"' `"YbbdP"' 88 `Y8a `"YbbdP"'
*/
hook OnScriptInit()
{
// Set up the linked list of free tag and K/V slots.
for (new i = 0; i != sizeof (YSI_g_sINITagBuffer) - 1; ++i)
{
YSI_g_sINITagBuffer[i][E_INI_TAGS_NEXT] = i + 1;
}
for (new i = 0; i != sizeof (YSI_g_sINIWriteBuffer) - 1; ++i)
{
YSI_g_sINIWriteBuffer[i][E_INI_KV_ENTRY_NEXT] = i + 1;
}
// Start and end value.
YSI_g_sINITagBuffer[sizeof (YSI_g_sINITagBuffer) - 1][E_INI_TAGS_NEXT] = YSI_g_sINIWriteBuffer[sizeof (YSI_g_sINIWriteBuffer) - 1][E_INI_KV_ENTRY_NEXT] = -1,
YSI_g_sINITagPos = YSI_g_sINIWritePos = 0;
}
/*
88 88
88 ,d 88
88 88 88
88 8b,dPPYba, MM88MMM ,adPPYba, 8b,dPPYba, 8b,dPPYba, ,adPPYYba, 88
88 88P' `"8a 88 a8P_____88 88P' "Y8 88P' `"8a "" `Y8 88
88 88 88 88 8PP""""""" 88 88 88 ,adPPPPP88 88
88 88 88 88, "8b, ,aa 88 88 88 88, ,88 88
88 88 88 "Y888 `"Ybbd8"' 88 88 88 `"8bbdP"Y8 88
*/
static stock INI_GetTag(INI:file, const tag[])
{
new
cur = YSI_g_sINIStartTag[file];
while (cur != -1)
{
if (!strcmp(tag, YSI_g_sINITagBuffer[cur][E_INI_TAGS_NAME], true))
{
return cur;
}
cur = YSI_g_sINITagBuffer[cur][E_INI_TAGS_NEXT];
}
return -1;
}
/*-------------------------------------------------------------------------*//**
* Slot to remove.
*
*
*
*//*------------------------------------------------------------------------**/
static stock INI_FreeEntry(slot)
{
new
ret = YSI_g_sINIWriteBuffer[slot][E_INI_KV_ENTRY_NEXT];
// Add this entry to the "free" list.
return
YSI_g_sINIWriteBuffer[slot][E_INI_KV_ENTRY_NEXT] = YSI_g_sINIWritePos,
YSI_g_sINIWritePos = slot,
ret;
}
static stock INI_DumpTag(File:buffer, curTag)
{
new
curSlot = YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START];
while (0 <= curSlot < sizeof (YSI_g_sINIWriteBuffer))
{
fwrite(buffer, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NAME]),
fwrite(buffer, " = "),
fwrite(buffer, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_TEXT]),
fwrite(buffer, INI_NEW_LINE),
curSlot = INI_FreeEntry(curSlot);
}
}
/*stock INI_WriteArray(INI:file, const name[], data[], len)
{
// Write 6 bits at a time, in 3 cell chunks. It takes 16 bytes to record
// three cells with 6 bits per byte.
P:4("INI_WriteArray called");
new
dname[MAX_INI_ENTRY_NAME],
write[Y_INI_WRITE_ARRAY_SIZE + 1],
idx,
wi,
iter;
// Write the length first just so the data exists.
//INI_WriteInt(file, name, len);
valstr(write, len),
INI_AddToBuffer(file, name, write),
write[0] = '\0';
while (idx + 3 < len)
{
// Store all the data fast.
write[wi++] = ((data[idx] & 0xFC000000) >>> 26) + '>',
write[wi++] = ((data[idx] & 0x03F00000) >>> 20) + '>',
write[wi++] = ((data[idx] & 0x000FC000) >>> 14) + '>',
write[wi++] = ((data[idx] & 0x00003F00) >>> 8) + '>',
write[wi++] = ((data[idx] & 0x000000FC) >>> 2) + '>',
write[wi++] = (((data[idx] & 0x00000003) << 4) | ((data[idx + 1] & 0xF0000000) >>> 28)) + '>',
++idx,
write[wi++] = ((data[idx] & 0x0FC00000) >>> 22) + '>',
write[wi++] = ((data[idx] & 0x003F0000) >>> 16) + '>',
write[wi++] = ((data[idx] & 0x0000FC00) >>> 10) + '>',
write[wi++] = ((data[idx] & 0x000003F0) >>> 4) + '>',
write[wi++] = (((data[idx] & 0x0000000F) << 2) | ((data[idx + 1] & 0xC0000000) >>> 30)) + '>',
++idx,
write[wi++] = ((data[idx] & 0x3F000000) >>> 24) + '>',
write[wi++] = ((data[idx] & 0x00FC0000) >>> 18) + '>',
write[wi++] = ((data[idx] & 0x0003F000) >>> 12) + '>',
write[wi++] = ((data[idx] & 0x00000FC0) >>> 6) + '>',
write[wi++] = ((data[idx] & 0x0000003F) >>> 0) + '>',
++idx;
if (wi == Y_INI_WRITE_ARRAY_SIZE)
{
format(dname, sizeof (dname), "@@%s-%d", name, iter++);
P:5("Uvar_WriteArray: write %s = %s", dname, write);
write[wi] = '\0',
INI_AddToBuffer(file, dname, write),
write[0] = '\0',
wi = 0;
}
}
// Do the straggling bytes.
if (idx != len)
{
write[wi++] = ((data[idx] & 0xFC000000) >>> 26) + '>',
write[wi++] = ((data[idx] & 0x03F00000) >>> 20) + '>',
write[wi++] = ((data[idx] & 0x000FC000) >>> 14) + '>',
write[wi++] = ((data[idx] & 0x00003F00) >>> 8) + '>',
write[wi++] = ((data[idx] & 0x000000FC) >>> 2) + '>';
if (++idx == len)
{
write[wi++] = ((data[idx - 1] & 0x00000003) << 4) + '>';
}
else
{
write[wi++] = (((data[idx - 1] & 0x00000003) << 4) | ((data[idx] & 0xF0000000) >>> 28)) + '>',
write[wi++] = ((data[idx] & 0x0FC00000) >>> 22) + '>',
write[wi++] = ((data[idx] & 0x003F0000) >>> 16) + '>',
write[wi++] = ((data[idx] & 0x0000FC00) >>> 10) + '>',
write[wi++] = ((data[idx] & 0x000003F0) >>> 4) + '>';
if (++idx == len)
{
write[wi++] = ((data[idx - 1] & 0x0000000F) << 2) + '>';
}
else
{
write[wi++] = (((data[idx - 1] & 0x0000000F) << 2) | ((data[idx] & 0xC0000000) >>> 30)) + '>',
write[wi++] = ((data[idx] & 0x3F000000) >>> 24) + '>',
write[wi++] = ((data[idx] & 0x00FC0000) >>> 18) + '>',
write[wi++] = ((data[idx] & 0x0003F000) >>> 12) + '>',
write[wi++] = ((data[idx] & 0x00000FC0) >>> 6) + '>',
write[wi++] = ((data[idx] & 0x0000003F) >>> 0) + '>';
}
}
format(dname, sizeof (dname), "@@%s-%d", name, iter++);
P:5("Uvar_WriteArray: write %s = %s", dname, write);
write[wi] = '\0',
INI_AddToBuffer(file, dname, write),
write[0] = '\0',
wi = 0;
}
return 1;
}*/
/*-------------------------------------------------------------------------*//**
* INI file to write to.
* Data name to write.
* Data to write.
*
* The slot written to, or -1 on failure.
*
*
* First checks the name doesn't already exist under the current tag header
* and if it does overwrites the current value. If not checks there's room
* in the buffer to write to and purges the buffer if not. Finally saves the
* data in the buffer for writing when required and adds the data to the
* relevant list for tag inclusion.
*
*//*------------------------------------------------------------------------**/
_Y_INI_STATIC stock INI_AddToBuffer(INI:file, const key[], const value[])
{
P:4("INI_AddToBuffer called: %i, \"%s\", \"%s\"", _:file, key, value);
if (INI_IsValid(file) && key[0])
{
// Get the current tag.
new
curTag = YSI_g_sINICurrentTag[file];
if (curTag == -1) curTag = INI_SetTag(file, INI_NO_TAG);
else
{
// Find if this entry already exists.
new
curSlot = YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START];
if (curSlot == cellmax) return -1; // Deleted tag, don't save.
else while (curSlot != -1)
{
if (!strcmp(key, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NAME], true))
{
return
strcpy(YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_TEXT], value, MAX_INI_ENTRY_TEXT),
curSlot;
}
curSlot = YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NEXT];
}
}
// Get a new slot.
if (YSI_g_sINIWritePos == -1)
{
// Free up some space.
if (!INI_Flush()) return -1;
curTag = YSI_g_sINICurrentTag[file];
}
// Get a free slot in the system.
new
newSlot = YSI_g_sINIWritePos;
YSI_g_sINIWritePos = YSI_g_sINIWriteBuffer[YSI_g_sINIWritePos][E_INI_KV_ENTRY_NEXT],
YSI_g_sINIWriteBuffer[newSlot][E_INI_KV_ENTRY_NEXT] = YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START],
YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START] = newSlot;
// Now write to it.
return
strcpy(YSI_g_sINIWriteBuffer[newSlot][E_INI_KV_ENTRY_NAME], key, MAX_INI_ENTRY_NAME),
strcpy(YSI_g_sINIWriteBuffer[newSlot][E_INI_KV_ENTRY_TEXT], value, MAX_INI_ENTRY_TEXT),
newSlot;
}
return -1;
}
static stock INI_FreeTag(INI:file, tag)
{
new
prev = -1,
curSlot = YSI_g_sINIStartTag[file];
while (curSlot != tag)
{
curSlot = YSI_g_sINITagBuffer[(prev = curSlot)][E_INI_TAGS_NEXT];
}
if (prev == -1) YSI_g_sINIStartTag[file] = YSI_g_sINITagBuffer[curSlot][E_INI_TAGS_NEXT];
else YSI_g_sINITagBuffer[prev][E_INI_TAGS_NEXT] = YSI_g_sINITagBuffer[curSlot][E_INI_TAGS_NEXT];
// Release this tag slot.
return
prev = YSI_g_sINITagBuffer[curSlot][E_INI_TAGS_NEXT],
YSI_g_sINITagBuffer[curSlot][E_INI_TAGS_NEXT] = YSI_g_sINITagPos,
YSI_g_sINITagPos = curSlot,
prev;
}
/*
I8, 8 ,8I 88 88
`8b d8b d8' "" ,d ""
"8, ,8"8, ,8" 88
Y8 8P Y8 8P 8b,dPPYba, 88 MM88MMM 88 8b,dPPYba, ,adPPYb,d8
`8b d8' `8b d8' 88P' "Y8 88 88 88 88P' `"8a a8" `Y88
`8a a8' `8a a8' 88 88 88 88 88 88 8b 88
`8a8' `8a8' 88 88 88, 88 88 88 "8a, ,d88
`8' `8' 88 88 "Y888 88 88 88 `"YbbdP"Y8
aa, ,88
"Y8bbdP"
*/
static stock bool:INI_Flush()
{
for (new INI:i = INI:0; i != INI:INI_MAX_WRITES; ++i)
{
if (YSI_g_sINIWriteFile[i][0])
{
new
curTag = YSI_g_sINICurrentTag[i],
tag2[MAX_INI_TAG] = "\1\0";
if (curTag != -1) strunpack(tag2, YSI_g_sINITagBuffer[curTag][E_INI_TAGS_NAME]);
if (!INI_WriteBuffer(i)) return false;
if (curTag != -1) INI_SetTag(i, tag2);
}
}
return true;
}
/*-------------------------------------------------------------------------*//**
* INI stream to write to file.
*
* Success/fail.
*
*
* Opens the required file for reading and a temp file for read/writing. Goes
* through the entire file reading all contained data. If it reaches a tag
* line ([tag_name]) it dumps any unwritten data from the last tag (if there
* was one) and starts processing the new tag. While a tag is being processed
* every line is compared against the UNWRITTEN new data for that tag in the
* buffer, if they're the same it writes the new data instead (it also writes
* any comments which were after the data in the original line back), else it
* writes the original line back.
*
* Once all the new data is written to the temp file any tags which haven't
* been processed at all (i.e. were not found in the original file) are
* written to the temp file along with all their data. The original file is
* then destroyed and reopend and all the data copied out from the temp file
* to the newly opened original file, closed and saved.
*
*//*------------------------------------------------------------------------**/
static stock bool:INI_WriteBuffer(INI:file)
{
if (!INI_IsValid(file)) return false;
static
sLine[MAX_INI_LINE];
new
File:buffer = ftemp();
if (!buffer) return false;
new
File:source = fopen(YSI_g_sINIWriteFile[file], io_read);
if (!source) return fclose(buffer), false;
// Start the processing. Code again copied from "INI_ParseFile".
new
p0s, p0e, p1s, p1e, p2s, p2e,
curTag = INI_GetTag(file, NULL), // Get the empty tag.
// See if this tag should be saved or not.
bool:deleted = INI_IsDeleted(curTag);
while (fread(source, sLine))
{
switch (INI_IdentifyLineType(sLine, p0s, p0e, p1s, p1e, p2s, p2e))
{
case e_INI_LINE_TYPE_INVALID:
P:I("Invalid line in INI file \"%s\": %s", YSI_g_sINIWriteFile[file], sLine);
case e_INI_LINE_TYPE_DATALESS:
if (!deleted) fwrite(buffer, sLine);
case e_INI_LINE_TYPE_TAG:
{
// Clean up the previous tag.
if (curTag != -1)
{
if (!deleted) INI_DumpTag(buffer, curTag);
// Get rid of this tag (now empty).
INI_FreeTag(file, curTag);
}
// Then get the new tag to write from.
new
ch = sLine[p0e];
sLine[p0e] = '\0',
curTag = INI_GetTag(file, sLine[p0s]);
if (!(deleted = INI_IsDeleted(curTag)))
{
sLine[p0e] = ch,
fwrite(buffer, sLine);
}
}
default: // e_INI_LINE_TYPE_ENTRY and e_INI_LINE_TYPE_CONT.
{
if (deleted) continue; // Delete the entry.
if (curTag == -1) fwrite(buffer, sLine); // No replacement data.
else
{
new
prev = -1,
curSlot = YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START];
p0e -= p0s;
while (curSlot != -1)
{
if (!strcmp(sLine[p0s], YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NAME], true, p0e))
{
fwrite(buffer, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NAME]),
fwrite(buffer, " = "),
fwrite(buffer, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_TEXT]);
// Write the end bits.
if (p2s != p2e) INI_WriteComments();
else fwrite(buffer, INI_NEW_LINE);
break;
}
curSlot = YSI_g_sINIWriteBuffer[(prev = curSlot)][E_INI_KV_ENTRY_NEXT];
}
if (curSlot == -1) fwrite(buffer, sLine); // Not replaced.
else
{
// Free this now written slot.
if (prev == -1)
{
if ((YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START] = YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NEXT]) == -1)
{
// No items left in the tag.
INI_FreeTag(file, curTag),
curTag = -1;
// Don't reset "deleted" though.
}
}
else YSI_g_sINIWriteBuffer[prev][E_INI_KV_ENTRY_NEXT] = YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NEXT];
YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NEXT] = YSI_g_sINIWritePos,
YSI_g_sINIWritePos = curSlot;
}
}
}
}
// Don't put any code down here (at the end of the loop).
}
// FIRST write out the rest of the current tag.
if (curTag != -1)
{
if (!deleted) INI_DumpTag(buffer, curTag);
INI_FreeTag(file, curTag);
}
// THEN do the remaining new tags.
curTag = YSI_g_sINIStartTag[file];
while (curTag != -1)
{
if (YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START] != cellmax)
{
new
tag[32];
// Write the tag's name.
strunpack(tag, YSI_g_sINITagBuffer[curTag][E_INI_TAGS_NAME]);
fwrite(buffer, "["),
fwrite(buffer, tag),
fwrite(buffer, "]" INI_NEW_LINE),
INI_DumpTag(buffer, curTag);
}
curTag = INI_FreeTag(file, curTag);
}
// Done writing, copy the results over.
fclose(source),
fremove(YSI_g_sINIWriteFile[file]),
fseek(buffer);
if (!(source = fopen(YSI_g_sINIWriteFile[file], io_write))) return fclose(buffer), false;
// Copy the buffer over.
while (fread(buffer, sLine)) fwrite(source, sLine);
return
fclose(buffer),
fclose(source),
true;
}