2023-06-18 10:09:36 +00:00
|
|
|
static config_entry *Config_FindEntryByName(config *Config, string Name)
|
|
|
|
{
|
|
|
|
config_entry *Result = 0;
|
|
|
|
|
|
|
|
u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets);
|
|
|
|
config_entry_bucket *Bucket = Config->EntryBuckets + BucketSlot;
|
|
|
|
|
|
|
|
for(config_entry *Entry = Bucket->First;
|
|
|
|
Entry != 0;
|
|
|
|
Entry = Entry->Next)
|
|
|
|
{
|
|
|
|
if(AreEqual(Entry->Name, Name))
|
|
|
|
{
|
|
|
|
Result = Entry;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(Result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Config_BindEntry(config *Config, string Name, config_entry_type Type, void *Target)
|
|
|
|
{
|
|
|
|
config_entry *Entry = Config_FindEntryByName(Config, Name);
|
|
|
|
if(!Entry)
|
|
|
|
{
|
|
|
|
Entry = PushStruct(&Config->Arena, config_entry);
|
2023-06-19 17:12:26 +00:00
|
|
|
Entry->Name = PushString(&Config->Arena, Name);
|
2023-06-18 10:09:36 +00:00
|
|
|
Entry->Type = Type;
|
|
|
|
|
|
|
|
u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets);
|
|
|
|
config_entry_bucket *Bucket = Config->EntryBuckets + BucketSlot;
|
|
|
|
|
|
|
|
DLLInsertLast(Bucket->First, Bucket->Last, Entry);
|
2023-06-19 17:12:26 +00:00
|
|
|
|
|
|
|
if(Config->LastInternal)
|
|
|
|
{
|
|
|
|
Config->LastInternal->NextInternal = Entry;
|
|
|
|
Config->LastInternal = Entry;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config->FirstInternal = Config->LastInternal = Entry;
|
|
|
|
}
|
2023-06-18 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Assert(Entry->Type == Type);
|
|
|
|
Entry->Target = Target;
|
2023-06-19 17:12:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void Config_BindS32(config *Config, string Name, s32 *Target, s32 Default)
|
|
|
|
{
|
|
|
|
*Target = Default;
|
|
|
|
Config_BindEntry(Config, Name, Config_Entry_S32, Target);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Config_BindS64(config *Config, string Name, s64 *Target, s64 Default)
|
|
|
|
{
|
|
|
|
*Target = Default;
|
|
|
|
Config_BindEntry(Config, Name, Config_Entry_S64, Target);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Config_BindB32(config *Config, string Name, b32 *Target, b32 Default)
|
|
|
|
{
|
|
|
|
*Target = Default;
|
|
|
|
Config_BindEntry(Config, Name, Config_Entry_B32, Target);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Config_ParseError(char *Message, memory_arena *Arena)
|
|
|
|
{
|
|
|
|
string String = PushFormat(Arena, "An error occured during config parsing:\n\"%s\"", Message);
|
|
|
|
Platform.ShowMessage(String, Platform_Message_Warning);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Config_ReadFile(config *Config, string Path)
|
|
|
|
{
|
|
|
|
temporary_memory Scratch = GetScratch();
|
|
|
|
|
|
|
|
tokenizer Tokenizer = Tokenizer_BeginTokenization(Platform_ReadEntireFile(Scratch.Arena, Path));
|
|
|
|
if(Tokenizer.Input.Data)
|
|
|
|
{
|
|
|
|
token Token;
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
Token = Tokenizer_GetNextToken(&Tokenizer);
|
|
|
|
|
|
|
|
if(Token.Type == Token_Identifier)
|
|
|
|
{
|
|
|
|
string Dir = Token.String;
|
|
|
|
|
|
|
|
if(Tokenizer_RequireToken(&Tokenizer, Token_CurlyOpen))
|
|
|
|
{
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
Token = Tokenizer_GetNextToken(&Tokenizer);
|
|
|
|
|
|
|
|
if(Token.Type == Token_Identifier)
|
|
|
|
{
|
|
|
|
string Name = Token.String;
|
|
|
|
|
|
|
|
if(Tokenizer_RequireToken(&Tokenizer, Token_Equals))
|
|
|
|
{
|
|
|
|
Token = Tokenizer_GetNextToken(&Tokenizer);
|
|
|
|
|
|
|
|
if(Token.Type == Token_IntegerValue)
|
|
|
|
{
|
|
|
|
s64 Value = ConvertStringToS64(Token.String);
|
|
|
|
|
|
|
|
string FullName = PushFormat(Scratch.Arena, "%S/%S", Dir, Name);
|
|
|
|
config_entry *Entry = Config_FindEntryByName(Config, FullName);
|
|
|
|
if(Entry)
|
|
|
|
{
|
|
|
|
if(Entry->Type == Config_Entry_S32)
|
|
|
|
{
|
|
|
|
*(s32 *)Entry->Target = (s32)Value;
|
|
|
|
}
|
|
|
|
else if(Entry->Type == Config_Entry_S64)
|
|
|
|
{
|
|
|
|
*(s64 *)Entry->Target = Value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Entry has wrong data type.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Cannot find entry.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(Token.Type == Token_Identifier)
|
|
|
|
{
|
|
|
|
string FullName = PushFormat(Scratch.Arena, "%S/%S", Dir, Name);
|
|
|
|
config_entry *Entry = Config_FindEntryByName(Config, FullName);
|
|
|
|
if(Entry)
|
|
|
|
{
|
|
|
|
if(AreEqual(Token.String, StrLit("true")))
|
|
|
|
{
|
|
|
|
*(b32 *)Entry->Target = true;
|
|
|
|
}
|
|
|
|
else if(AreEqual(Token.String, StrLit("false")))
|
|
|
|
{
|
|
|
|
*(b32 *)Entry->Target = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Entry has wrong data type.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Cannot find entry.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Expected a value.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!Tokenizer_RequireToken(&Tokenizer, Token_Semicolon))
|
|
|
|
{
|
|
|
|
Config_ParseError("Expected a ';'.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Expected '='.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(Token.Type == Token_CurlyClose)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Expected '}' or identifier.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Expected '{'.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(Token.Type == Token_EndOfFile)
|
|
|
|
{
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Config_ParseError("Unexpected token.", Scratch.Arena);
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
End:
|
|
|
|
ReleaseScratch(Scratch);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Config_WriteFile(config *Config)
|
|
|
|
{
|
|
|
|
string_list Out = {};
|
|
|
|
temporary_memory Scratch = GetScratch();
|
|
|
|
|
|
|
|
string LastDir = MakeString(0, 0);
|
|
|
|
for(config_entry *Entry = Config->FirstInternal;
|
|
|
|
Entry != 0;
|
|
|
|
Entry = Entry->NextInternal)
|
|
|
|
{
|
|
|
|
s64 LastSlash = LastIndexOf(Entry->Name, '/');
|
|
|
|
Assert(LastSlash != -1);
|
|
|
|
|
|
|
|
string Dir = Prefix(Entry->Name, LastSlash);
|
|
|
|
string Name = Suffix(Entry->Name, Entry->Name.Count - LastSlash - 1);
|
|
|
|
|
|
|
|
if(!AreEqual(Dir, LastDir))
|
|
|
|
{
|
|
|
|
if(!AreEqual(LastDir, MakeString(0, 0)))
|
|
|
|
{
|
|
|
|
AppendString(&Out, StrLit("}\n\n"), Scratch.Arena);
|
|
|
|
}
|
|
|
|
|
|
|
|
AppendString(&Out, Dir, Scratch.Arena);
|
|
|
|
AppendString(&Out, StrLit("\n{\n"), Scratch.Arena);
|
|
|
|
|
|
|
|
LastDir = Dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
AppendString(&Out, StrLit("\t"), Scratch.Arena);
|
|
|
|
AppendString(&Out, Name, Scratch.Arena);
|
|
|
|
AppendString(&Out, StrLit(" = "), Scratch.Arena);
|
|
|
|
|
|
|
|
// sixten: Output the value of the entry
|
|
|
|
if(Entry->Type == Config_Entry_S32 || Entry->Type == Config_Entry_S64)
|
|
|
|
{
|
|
|
|
s64 IntegerValue;
|
|
|
|
if(Entry->Type == Config_Entry_S32)
|
|
|
|
{
|
|
|
|
IntegerValue = *(s32 *)Entry->Target;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IntegerValue = *(s64 *)Entry->Target;
|
|
|
|
}
|
|
|
|
|
2023-06-21 16:59:36 +00:00
|
|
|
string Value = ConvertS64ToString(Scratch.Arena, IntegerValue);
|
2023-06-19 17:12:26 +00:00
|
|
|
AppendString(&Out, Value, Scratch.Arena);
|
|
|
|
}
|
|
|
|
else if(Entry->Type == Config_Entry_B32)
|
|
|
|
{
|
|
|
|
string Value = (*(b32 *)Entry->Target)?StrLit("true"):StrLit("false");
|
|
|
|
AppendString(&Out, Value, Scratch.Arena);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UnimplementedCodepath;
|
|
|
|
}
|
|
|
|
|
|
|
|
AppendString(&Out, StrLit(";\n"), Scratch.Arena);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!AreEqual(LastDir, MakeString(0, 0)))
|
|
|
|
{
|
|
|
|
AppendString(&Out, StrLit("}"), Scratch.Arena);
|
|
|
|
}
|
|
|
|
|
|
|
|
string FinalOut = JoinStringList(&Out, Scratch.Arena);
|
|
|
|
|
|
|
|
platform_file_handle Handle = Platform.OpenFile(StrLit("config.vn"), PlatformAccess_Write);
|
|
|
|
if(Handle.IsValid)
|
|
|
|
{
|
|
|
|
Platform.WriteFile(Handle, FinalOut.Data, 0, FinalOut.Count);
|
|
|
|
Platform.CloseFile(Handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseScratch(Scratch);
|
2023-06-18 10:09:36 +00:00
|
|
|
}
|