vn/code/vn_config.cpp

287 lines
10 KiB
C++

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);
Entry->Name = PushString(&Config->Arena, Name);
Entry->Type = Type;
u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets);
config_entry_bucket *Bucket = Config->EntryBuckets + BucketSlot;
DLLInsertLast(Bucket->First, Bucket->Last, Entry);
if(Config->LastInternal)
{
Config->LastInternal->NextInternal = Entry;
Config->LastInternal = Entry;
}
else
{
Config->FirstInternal = Config->LastInternal = Entry;
}
}
Assert(Entry->Type == Type);
Entry->Target = Target;
}
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;
}
string Value = ConvertS64ToString(IntegerValue, Scratch.Arena);
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);
}