Succesffuly implemented resource pack loading for configurations files. Improved loading speed times for phase 3.5,4,and 5 of loading process. Stage loading is currently broken. Release Build 12609.
All checks were successful
Emscripten Build / Build_and_Deploy_Web_Build (push) Successful in 7m17s
All checks were successful
Emscripten Build / Build_and_Deploy_Web_Build (push) Successful in 7m17s
This commit is contained in:
parent
2b541386ce
commit
c7ce048e51
@ -753,7 +753,7 @@
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TileGroup.h" />
|
||||
<ClInclude Include="TileGroupSaveData.h" />
|
||||
<ClInclude Include="TileGroupDataFile.h" />
|
||||
<ClInclude Include="Timer.h" />
|
||||
<ClInclude Include="TitleScreen.h">
|
||||
<SubType>
|
||||
|
||||
@ -735,7 +735,7 @@
|
||||
<ClInclude Include="FallEffect.h">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TileGroupSaveData.h">
|
||||
<ClInclude Include="TileGroupDataFile.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TileGroup.h">
|
||||
|
||||
@ -86,7 +86,7 @@ All rights reserved.
|
||||
#include <stacktrace>
|
||||
#endif
|
||||
#include <ranges>
|
||||
#include"TileGroupSaveData.h"
|
||||
#include"TileGroupDataFile.h"
|
||||
|
||||
INCLUDE_EMITTER_LIST
|
||||
INCLUDE_ITEM_CATEGORIES
|
||||
@ -2513,6 +2513,13 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
|
||||
|
||||
#pragma region Foreground and Upper Foreground Tile Fade Group Setup (Loading phase 4)
|
||||
LoadingScreen::AddPhase([&](){
|
||||
const std::string foregroundTileDataFilename{std::format("assets/cache/foregroundTileData_{}.data",GetCurrentLevel())};
|
||||
const std::string upperForegroundTileDataFilename{std::format("assets/cache/upperForegroundTileData_{}.data",GetCurrentLevel())};
|
||||
if(gamepack.FileExists(foregroundTileDataFilename)&&gamepack.FileExists(upperForegroundTileDataFilename)){
|
||||
TileGroupDataFile::Load(gamepack,foregroundTileDataFilename,foregroundTileGroups);
|
||||
TileGroupDataFile::Load(gamepack,upperForegroundTileDataFilename,upperForegroundTileGroups);
|
||||
return true;
|
||||
}
|
||||
std::set<vi2d>foregroundTilesAdded,upperForegroundTilesAdded;
|
||||
for(int x=0;x<GetCurrentMapData().width;x++){
|
||||
for(int y=0;y<GetCurrentMapData().height;y++){
|
||||
@ -2605,6 +2612,9 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
|
||||
|
||||
#pragma region Foreground and Upper Foreground Tile Fade Group Individual Object Grouping Splitting (Loading phase 5)
|
||||
LoadingScreen::AddPhase([&](){
|
||||
const std::string foregroundTileDataFilename{std::format("assets/cache/foregroundTileData_{}.data",GetCurrentLevel())};
|
||||
const std::string upperForegroundTileDataFilename{std::format("assets/cache/upperForegroundTileData_{}.data",GetCurrentLevel())};
|
||||
if(gamepack.FileExists(foregroundTileDataFilename)&&gamepack.FileExists(upperForegroundTileDataFilename))return true; //We already loaded the structures in the last loading phase, so we simply do not do anything here.
|
||||
auto SplitUp=[&](std::vector<TileGroup>&group){
|
||||
std::multimap<vi2d,TileRenderData>data;
|
||||
using TileDataGroup=std::multimap<vi2d,TileRenderData>; //See below.
|
||||
@ -2675,8 +2685,8 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
|
||||
std::sort(upperForegroundTileGroups.begin(),upperForegroundTileGroups.end(),[&](TileGroup&group1,TileGroup&group2){
|
||||
return group1.GetCollisionRange().middle().y<group2.GetCollisionRange().middle().y;
|
||||
});
|
||||
TileGroupSaveData foregroundTilesSaveData{gamepack,std::format("assets/cache/foregroundTileData_{}.data",GetCurrentMapName()),foregroundTileGroups};
|
||||
TileGroupSaveData upperForegroundTilesSaveData{gamepack,std::format("assets/cache/upperForegroundTileData_{}.data",GetCurrentMapName()),upperForegroundTileGroups};
|
||||
TileGroupDataFile::Save(gamepack,std::format("assets/cache/foregroundTileData_{}.data",GetCurrentMapName()),foregroundTileGroups);
|
||||
TileGroupDataFile::Save(gamepack,std::format("assets/cache/upperForegroundTileData_{}.data",GetCurrentMapName()),upperForegroundTileGroups);
|
||||
return true;
|
||||
});
|
||||
#pragma endregion
|
||||
@ -2780,6 +2790,9 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
|
||||
ClearGarbage();
|
||||
|
||||
GetPlayer()->OnLevelStart();
|
||||
|
||||
|
||||
gamepack.SavePack("assets/"+"gamepack_file"_S,PACK_KEY);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ All rights reserved.
|
||||
#include "Map.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include "safemap.h"
|
||||
#include"TileGroupSaveData.h"
|
||||
#include"TileGroupDataFile.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
|
||||
@ -45,6 +45,9 @@ INCLUDE_game
|
||||
INCLUDE_GFX
|
||||
|
||||
void Minimap::Initialize(){
|
||||
Minimap::minimapChunkSize="Minimap.Chunk Size"_I;
|
||||
Minimap::minimapMapExploreTransparency="Minimap.Map Explore Transparency"_I;
|
||||
|
||||
std::vector<vf2d>enlargedCircle;
|
||||
for(int i=360;i>=0;i-=4){
|
||||
float angle=util::degToRad(float(i))-PI/2;
|
||||
@ -195,10 +198,10 @@ void Minimap::UpdateChunk(const MapName map,const vi2d chunkPos,const InitialLoa
|
||||
|
||||
if(game->GetCurrentMapName()!=map)return; //Don't update the minimap when the map name doesn't match the current map.
|
||||
|
||||
vi2d centerChunkPos=chunkPos*"Minimap.Chunk Size"_I;
|
||||
vi2d centerChunkPos=chunkPos*Minimap::minimapChunkSize;
|
||||
|
||||
vi2d pixelPos=centerChunkPos-"Minimap.Chunk Size"_I*2;
|
||||
vi2d chunkEndPixelPos=centerChunkPos+"Minimap.Chunk Size"_I*4;
|
||||
vi2d pixelPos=centerChunkPos-Minimap::minimapChunkSize*2;
|
||||
vi2d chunkEndPixelPos=centerChunkPos+Minimap::minimapChunkSize*4;
|
||||
|
||||
//We start twice the distance we are supposed to outwards.
|
||||
for(int y=pixelPos.y;y<chunkEndPixelPos.y;y++){
|
||||
@ -209,21 +212,21 @@ void Minimap::UpdateChunk(const MapName map,const vi2d chunkPos,const InitialLoa
|
||||
|
||||
if(cover.Sprite()->GetPixel(x,y).a==255||sourceCol.a==0)continue; //Already revealed or invisible anyways.
|
||||
|
||||
vi2d chunk=vi2d{x,y}/"Minimap.Chunk Size"_I;
|
||||
vi2d chunk=vi2d{x,y}/Minimap::minimapChunkSize;
|
||||
if(chunk==chunkPos){
|
||||
if(initialLoad==InitialLoad::YES)sourceCol.a=std::min(sourceCol.a,uint8_t("Minimap.Map Explore Transparency"_I));
|
||||
if(initialLoad==InitialLoad::YES)sourceCol.a=std::min(sourceCol.a,uint8_t(Minimap::minimapMapExploreTransparency));
|
||||
cover.Sprite()->SetPixel(x,y,sourceCol);
|
||||
if(sourceWasBlack)coverOutline.Sprite()->SetPixel(x,y,sourceCol);
|
||||
}else{
|
||||
const vi2d chunkOffset={"Minimap.Chunk Size"_I/2,"Minimap.Chunk Size"_I/2};
|
||||
const vi2d chunkOffset={Minimap::minimapChunkSize/2,Minimap::minimapChunkSize/2};
|
||||
|
||||
const float distance=geom2d::line<float>(centerChunkPos+chunkOffset,vf2d{float(x),float(y)}).length();
|
||||
const int alpha=std::clamp(float(util::lerp(255,0,(distance-"Minimap.Chunk Size"_I)/"Minimap.Chunk Size"_I)),0.f,255.f);
|
||||
const int alpha=std::clamp(float(util::lerp(255,0,(distance-Minimap::minimapChunkSize)/Minimap::minimapChunkSize)),0.f,255.f);
|
||||
|
||||
if(cover.Sprite()->GetPixel(x,y).a>alpha)continue; //The distance was uncovered by another closer chunk, don't need to reveal it here.
|
||||
|
||||
sourceCol.a=alpha;
|
||||
if(initialLoad==InitialLoad::YES)sourceCol.a=std::min(sourceCol.a,uint8_t("Minimap.Map Explore Transparency"_I));
|
||||
if(initialLoad==InitialLoad::YES)sourceCol.a=std::min(sourceCol.a,uint8_t(Minimap::minimapMapExploreTransparency));
|
||||
cover.Sprite()->SetPixel(x,y,sourceCol);
|
||||
if(sourceWasBlack)coverOutline.Sprite()->SetPixel(x,y,sourceCol);
|
||||
}
|
||||
|
||||
@ -73,5 +73,8 @@ private:
|
||||
std::unordered_map<MapName,std::unordered_set<std::string>>loadedChunks,revealedChunks;
|
||||
MinimapMode displayMode=MinimapMode::SMALL;
|
||||
|
||||
static inline int minimapChunkSize{};
|
||||
static inline int minimapMapExploreTransparency{};
|
||||
|
||||
bool MinimapVisible();
|
||||
};
|
||||
@ -55,7 +55,7 @@ All rights reserved.
|
||||
#include "ItemEnchant.h"
|
||||
#include <ranges>
|
||||
#include"MonsterAbility.h"
|
||||
#include"TileGroupSaveData.h"
|
||||
#include"TileGroupDataFile.h"
|
||||
|
||||
INCLUDE_ANIMATION_DATA
|
||||
INCLUDE_MONSTER_DATA
|
||||
|
||||
@ -39,10 +39,12 @@ All rights reserved.
|
||||
|
||||
#include"olcPixelGameEngine.h"
|
||||
#include"Map.h"
|
||||
#include"DEFINES.h"
|
||||
|
||||
INCLUDE_PACK_KEY
|
||||
|
||||
struct TileGroup{
|
||||
friend class TileGroupSaveData;
|
||||
friend class TileGroupLoadData;
|
||||
friend class TileGroupDataFile;
|
||||
private:
|
||||
geom2d::rect<int>range;
|
||||
geom2d::rect<float>collisionRange={{},{}};
|
||||
@ -61,46 +63,52 @@ public:
|
||||
float fadeFactor=0.f;
|
||||
};
|
||||
|
||||
class TileGroupLoadData{
|
||||
public:
|
||||
TileGroupLoadData(ResourcePack&pack,const std::string& loadFilename,std::vector<TileGroup>&tilegroups){
|
||||
datafile data;
|
||||
datafile::Read(data,loadFilename);
|
||||
tilegroups.clear();
|
||||
for(const auto&[key,size]:data.GetOrderedKeys()){
|
||||
enum TileReadTag:uint32_t{
|
||||
TILESET_NAME,
|
||||
FIRSTGID,
|
||||
TILECOL,
|
||||
POS_X,
|
||||
POS_Y,
|
||||
TILESHEETPOS_X,
|
||||
TILESHEETPOS_Y,
|
||||
TILEID,
|
||||
LAYERID,
|
||||
};
|
||||
|
||||
class TileGroupDataFile{
|
||||
public:
|
||||
static void Load(ResourcePack&pack,const std::string& loadFilename,std::vector<TileGroup>&tilegroups){
|
||||
datafile data;
|
||||
datafile::Read(data,loadFilename,pack);
|
||||
tilegroups.clear();
|
||||
for(const auto&[groupKey,size]:data.GetOrderedKeys()){ //Iterate through groups
|
||||
for(const auto&[key,size]:data[groupKey].GetOrderedKeys()){ //Iterate through properties of groups
|
||||
datafile&propData{data[groupKey][key]};
|
||||
TileGroup&newGroup{tilegroups.emplace_back()};
|
||||
if(key=="data"){
|
||||
size_t itemInd{};
|
||||
newGroup.range.pos.x=data[key].GetInt(itemInd++);
|
||||
newGroup.range.pos.y=data[key].GetInt(itemInd++);
|
||||
newGroup.range.size.x=data[key].GetInt(itemInd++);
|
||||
newGroup.range.size.y=data[key].GetInt(itemInd++);
|
||||
newGroup.collisionRange.pos.x=data[key].GetReal(itemInd++);
|
||||
newGroup.collisionRange.pos.y=data[key].GetReal(itemInd++);
|
||||
newGroup.collisionRange.size.x=data[key].GetReal(itemInd++);
|
||||
newGroup.collisionRange.size.y=data[key].GetReal(itemInd++);
|
||||
newGroup.minX=data[key].GetInt(itemInd++);
|
||||
newGroup.minY=data[key].GetInt(itemInd++);
|
||||
newGroup.maxX=data[key].GetInt(itemInd++);
|
||||
newGroup.maxY=data[key].GetInt(itemInd++);
|
||||
newGroup.range.pos.x=propData.GetInt(itemInd++);
|
||||
newGroup.range.pos.y=propData.GetInt(itemInd++);
|
||||
newGroup.range.size.x=propData.GetInt(itemInd++);
|
||||
newGroup.range.size.y=propData.GetInt(itemInd++);
|
||||
newGroup.collisionRange.pos.x=propData.GetReal(itemInd++);
|
||||
newGroup.collisionRange.pos.y=propData.GetReal(itemInd++);
|
||||
newGroup.collisionRange.size.x=propData.GetReal(itemInd++);
|
||||
newGroup.collisionRange.size.y=propData.GetReal(itemInd++);
|
||||
newGroup.minX=propData.GetInt(itemInd++);
|
||||
newGroup.minY=propData.GetInt(itemInd++);
|
||||
newGroup.maxX=propData.GetInt(itemInd++);
|
||||
newGroup.maxY=propData.GetInt(itemInd++);
|
||||
}else
|
||||
if(key.starts_with("tileRenderData")){
|
||||
size_t itemInd{};
|
||||
TileRenderData&newTileData{newGroup.tiles.emplace_back()};
|
||||
newTileData.pos.x=data[key].GetReal(itemInd++);
|
||||
newTileData.pos.y=data[key].GetReal(itemInd++);
|
||||
newTileData.tileSheetPos.x=data[key].GetInt(itemInd++);
|
||||
newTileData.tileSheetPos.y=data[key].GetInt(itemInd++);
|
||||
newTileData.tileID=data[key].GetInt(itemInd++);
|
||||
newTileData.layerID=data[key].GetInt(itemInd++);
|
||||
TilesheetData newTilesheetData{propData.GetString(TILESET_NAME),propData.GetInt(FIRSTGID),Pixel{uint32_t(propData.GetInt(TILECOL))}};
|
||||
TileRenderData newData{newTilesheetData,vf2d{propData.GetReal(POS_X),propData.GetReal(POS_Y)},vi2d{propData.GetInt(TILESHEETPOS_X),propData.GetInt(TILESHEETPOS_Y)},propData.GetInt(TILEID),propData.GetInt(LAYERID)};
|
||||
TileRenderData&newTileData{newGroup.tiles.emplace_back(newData)};
|
||||
}else ERR("WARNING! Unknown key "<<key<<" found while trying to load cache for file "<<loadFilename);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class TileGroupSaveData{
|
||||
public:
|
||||
TileGroupSaveData(ResourcePack&pack,const std::string& saveFilename,const std::vector<TileGroup>&tilegroups){
|
||||
}
|
||||
};
|
||||
static void Save(ResourcePack&pack,const std::string& saveFilename,const std::vector<TileGroup>&tilegroups){
|
||||
const bool prevSetupState{datafile::INITIAL_SETUP_COMPLETE};
|
||||
datafile::INITIAL_SETUP_COMPLETE=false;//Required to write to datafiles.
|
||||
datafile saveData;
|
||||
@ -122,14 +130,16 @@ public:
|
||||
dataProps.SetInt(group.maxY,itemInd++);
|
||||
TileRenderData:
|
||||
for(size_t tileRenderInd{};const TileRenderData&tileData:group.tiles){
|
||||
size_t itemInd{};
|
||||
datafile&tileRenderData{groupData[std::format("tileRenderData{}",tileRenderInd)]};
|
||||
tileRenderData.SetReal(tileData.pos.x,itemInd++);
|
||||
tileRenderData.SetReal(tileData.pos.y,itemInd++);
|
||||
tileRenderData.SetInt(tileData.tileSheetPos.x,itemInd++);
|
||||
tileRenderData.SetInt(tileData.tileSheetPos.y,itemInd++);
|
||||
tileRenderData.SetInt(tileData.tileID,itemInd++);
|
||||
tileRenderData.SetInt(tileData.layerID,itemInd++);
|
||||
tileRenderData.SetReal(tileData.pos.x,POS_X);
|
||||
tileRenderData.SetReal(tileData.pos.y,POS_Y);
|
||||
tileRenderData.SetInt(tileData.tileSheetPos.x,TILESHEETPOS_X);
|
||||
tileRenderData.SetInt(tileData.tileSheetPos.y,TILESHEETPOS_Y);
|
||||
tileRenderData.SetInt(tileData.tileID,TILEID);
|
||||
tileRenderData.SetInt(tileData.layerID,LAYERID);
|
||||
tileRenderData.SetString(tileData.tileSheet.tilesetName,TILESET_NAME);
|
||||
tileRenderData.SetInt(tileData.tileSheet.firstgid,FIRSTGID);
|
||||
tileRenderData.SetInt(tileData.tileSheet.tilecol.n,TILECOL);
|
||||
tileRenderInd++;
|
||||
}
|
||||
ind++;
|
||||
@ -39,7 +39,7 @@ All rights reserved.
|
||||
#define VERSION_MAJOR 1
|
||||
#define VERSION_MINOR 3
|
||||
#define VERSION_PATCH 0
|
||||
#define VERSION_BUILD 12581
|
||||
#define VERSION_BUILD 12609
|
||||
|
||||
#define stringify(a) stringify_(a)
|
||||
#define stringify_(a) #a
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -731,6 +731,7 @@ namespace olc
|
||||
bool SavePack(const std::string& sFile, const std::string& sKey);
|
||||
ResourceBuffer GetFileBuffer(const std::string& sFile);
|
||||
bool Loaded();
|
||||
const bool FileExists(const std::string&filename)const;
|
||||
private:
|
||||
struct sResourceFile { uint32_t nSize; uint32_t nOffset; };
|
||||
std::map<std::string, sResourceFile> mapFiles;
|
||||
@ -1952,6 +1953,10 @@ namespace olc
|
||||
bool ResourcePack::Loaded()
|
||||
{ return baseFile.is_open(); }
|
||||
|
||||
const bool ResourcePack::FileExists(const std::string&filename)const{
|
||||
return mapFiles.count(filename)>0;
|
||||
}
|
||||
|
||||
std::vector<char> ResourcePack::scramble(const std::vector<char>& data, const std::string& key)
|
||||
{
|
||||
if (key.empty()) return data;
|
||||
|
||||
@ -61,6 +61,7 @@ David Barr, aka javidx9, <20>OneLoneCoder 2019, 2020, 2021, 2022
|
||||
#include <fstream>
|
||||
#include <stack>
|
||||
#include <sstream>
|
||||
#include <strstream>
|
||||
#include <numeric>
|
||||
#include "Error.h"
|
||||
#include<ranges>
|
||||
@ -329,188 +330,200 @@ namespace olc::utils
|
||||
return false;
|
||||
}
|
||||
|
||||
inline static bool Read(datafile& n, const std::string& sFileName, olc::ResourcePack&pack, const char sListSep = ',', const OverwriteMode mode=OverwriteMode::NO_OVERWRITE)
|
||||
{
|
||||
ResourceBuffer buf{pack.GetFileBuffer(sFileName)};
|
||||
if(buf.vMemory.size()==0)return true; //Nothing to read.
|
||||
buf.vMemory.emplace_back('\0');
|
||||
return _Read(n,buf.vMemory,sListSep,mode);
|
||||
}
|
||||
|
||||
inline static bool Read(datafile& n, const std::string& sFileName, const char sListSep = ',', const OverwriteMode mode=OverwriteMode::NO_OVERWRITE)
|
||||
{
|
||||
std::ifstream fileStream{sFileName};
|
||||
if(fileStream.fail()){
|
||||
// File not found, so fail
|
||||
ERR("WARNING! Could not open file "<<sFileName<<"!");
|
||||
return false;
|
||||
}
|
||||
std::vector<char>data;
|
||||
while(!fileStream.eof())data.emplace_back(fileStream.get());
|
||||
data.emplace_back('\0');
|
||||
if(data.size()==0)return true; //Nothing to read.
|
||||
return _Read(n,data,sListSep,mode);
|
||||
}
|
||||
|
||||
private:
|
||||
inline static bool _Read(datafile& n,std::vector<char>&data, const char sListSep = ',', const OverwriteMode mode=OverwriteMode::NO_OVERWRITE){
|
||||
bool previousSetupState=INITIAL_SETUP_COMPLETE;
|
||||
INITIAL_SETUP_COMPLETE=false;
|
||||
// Open the file!
|
||||
std::ifstream file(sFileName);
|
||||
if (file.is_open())
|
||||
if(data.size()==0)ERR("WARNING! Nothing to read when trying to read from datafile!!");
|
||||
std::istringstream inputStream{data.data()};
|
||||
// These variables are outside of the read loop, as we will
|
||||
// need to refer to previous iteration values in certain conditions
|
||||
std::string sPropName = "";
|
||||
std::string sPropValue = "";
|
||||
|
||||
// The file is fundamentally structured as a stack, so we will read it
|
||||
// in a such, but note the data structure in memory is not explicitly
|
||||
// stored in a stack, but one is constructed implicitly via the nodes
|
||||
// owning other nodes (aka a tree)
|
||||
|
||||
// I dont want to accidentally create copies all over the place, nor do
|
||||
// I want to use pointer syntax, so being a bit different and stupidly
|
||||
// using std::reference_wrapper, so I can store references to datafile
|
||||
// nodes in a std::container.
|
||||
std::stack<std::reference_wrapper<datafile>> stkPath;
|
||||
stkPath.push(n);
|
||||
|
||||
|
||||
// Read file line by line and process
|
||||
while (!inputStream.eof())
|
||||
{
|
||||
// These variables are outside of the read loop, as we will
|
||||
// need to refer to previous iteration values in certain conditions
|
||||
std::string sPropName = "";
|
||||
std::string sPropValue = "";
|
||||
// Read line
|
||||
std::string line;
|
||||
std::getline(inputStream, line);
|
||||
|
||||
// The file is fundamentally structured as a stack, so we will read it
|
||||
// in a such, but note the data structure in memory is not explicitly
|
||||
// stored in a stack, but one is constructed implicitly via the nodes
|
||||
// owning other nodes (aka a tree)
|
||||
|
||||
// I dont want to accidentally create copies all over the place, nor do
|
||||
// I want to use pointer syntax, so being a bit different and stupidly
|
||||
// using std::reference_wrapper, so I can store references to datafile
|
||||
// nodes in a std::container.
|
||||
std::stack<std::reference_wrapper<datafile>> stkPath;
|
||||
stkPath.push(n);
|
||||
|
||||
|
||||
// Read file line by line and process
|
||||
while (!file.eof())
|
||||
// This little lambda removes whitespace from
|
||||
// beginning and end of supplied string
|
||||
auto trim = [](std::string& s)
|
||||
{
|
||||
// Read line
|
||||
std::string line;
|
||||
std::getline(file, line);
|
||||
s.erase(0, s.find_first_not_of(" \t\n\r\f\v"));
|
||||
s.erase(s.find_last_not_of(" \t\n\r\f\v") + 1);
|
||||
};
|
||||
|
||||
// This little lambda removes whitespace from
|
||||
// beginning and end of supplied string
|
||||
auto trim = [](std::string& s)
|
||||
trim(line);
|
||||
|
||||
// If line has content
|
||||
if (!line.empty())
|
||||
{
|
||||
// Test if its a comment...
|
||||
if (line[0] == '#')
|
||||
{
|
||||
s.erase(0, s.find_first_not_of(" \t\n\r\f\v"));
|
||||
s.erase(s.find_last_not_of(" \t\n\r\f\v") + 1);
|
||||
};
|
||||
|
||||
trim(line);
|
||||
|
||||
// If line has content
|
||||
if (!line.empty())
|
||||
// ...it is a comment, so ignore
|
||||
datafile comment;
|
||||
comment.m_bIsComment = true;
|
||||
stkPath.top().get().m_vecObjects.push_back({ line, comment });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Test if its a comment...
|
||||
if (line[0] == '#')
|
||||
// ...it is content, so parse. Firstly, find if the line
|
||||
// contains an assignment. If it does then it's a property...
|
||||
size_t x = line.find_first_of('=');
|
||||
if (x != std::string::npos)
|
||||
{
|
||||
// ...it is a comment, so ignore
|
||||
datafile comment;
|
||||
comment.m_bIsComment = true;
|
||||
stkPath.top().get().m_vecObjects.push_back({ line, comment });
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...it is content, so parse. Firstly, find if the line
|
||||
// contains an assignment. If it does then it's a property...
|
||||
size_t x = line.find_first_of('=');
|
||||
if (x != std::string::npos)
|
||||
// ...so split up the property into a name, and its values!
|
||||
|
||||
// Extract the property name, which is all characters up to
|
||||
// first assignment, trim any whitespace from ends
|
||||
sPropName = line.substr(0, x);
|
||||
trim(sPropName);
|
||||
auto&top=stkPath.top().get();
|
||||
if(mode==OverwriteMode::NO_OVERWRITE&&stkPath.top().get().HasProperty(sPropName))ERR(std::format("WARNING! Duplicate key found! Key {} already exists! Duplicate line: {}",sPropName,line));
|
||||
|
||||
// Extract the property value, which is all characters after
|
||||
// the first assignment operator, trim any whitespace from ends
|
||||
sPropValue = line.substr(x + 1, line.size());
|
||||
trim(sPropValue);
|
||||
|
||||
// The value may be in list form: a, b, c, d, e, f etc and some of those
|
||||
// elements may exist in quotes a, b, c, "d, e", f. So we need to iterate
|
||||
// character by character and break up the value
|
||||
bool bInQuotes = false;
|
||||
bool bEscapeChar = false;
|
||||
std::string sToken;
|
||||
size_t nTokenCount = 0;
|
||||
for (const auto c : sPropValue)
|
||||
{
|
||||
// ...so split up the property into a name, and its values!
|
||||
|
||||
// Extract the property name, which is all characters up to
|
||||
// first assignment, trim any whitespace from ends
|
||||
sPropName = line.substr(0, x);
|
||||
trim(sPropName);
|
||||
auto&top=stkPath.top().get();
|
||||
if(mode==OverwriteMode::NO_OVERWRITE&&stkPath.top().get().HasProperty(sPropName))ERR(std::format("WARNING! Duplicate key found! Key {} already exists! Duplicate line: {}",sPropName,line));
|
||||
|
||||
// Extract the property value, which is all characters after
|
||||
// the first assignment operator, trim any whitespace from ends
|
||||
sPropValue = line.substr(x + 1, line.size());
|
||||
trim(sPropValue);
|
||||
|
||||
// The value may be in list form: a, b, c, d, e, f etc and some of those
|
||||
// elements may exist in quotes a, b, c, "d, e", f. So we need to iterate
|
||||
// character by character and break up the value
|
||||
bool bInQuotes = false;
|
||||
bool bEscapeChar = false;
|
||||
std::string sToken;
|
||||
size_t nTokenCount = 0;
|
||||
for (const auto c : sPropValue)
|
||||
if (bEscapeChar&&c == 'n') //Parse a newline.
|
||||
{
|
||||
if (bEscapeChar&&c == 'n') //Parse a newline.
|
||||
{
|
||||
sToken.append(1, '\n');
|
||||
bEscapeChar=false;
|
||||
continue;
|
||||
}
|
||||
sToken.append(1, '\n');
|
||||
bEscapeChar=false;
|
||||
// Is character a quote...
|
||||
if (c == '\"')
|
||||
{
|
||||
// ...yes, so toggle quote state
|
||||
bInQuotes = !bInQuotes;
|
||||
}
|
||||
else
|
||||
if (c == '\\')
|
||||
{
|
||||
// ...yes, so toggle quote state
|
||||
bEscapeChar=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...no, so proceed creating token. If we are in quote state
|
||||
// then just append characters until we exit quote state.
|
||||
if (bInQuotes)
|
||||
{
|
||||
sToken.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Is the character our seperator? If it is
|
||||
if (c == sListSep)
|
||||
{
|
||||
// Clean up the token
|
||||
trim(sToken);
|
||||
// Add it to the vector of values for this property
|
||||
stkPath.top().get()[sPropName].SetString(sToken, nTokenCount);
|
||||
// Reset our token state
|
||||
sToken.clear();
|
||||
nTokenCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It isnt, so just append to token
|
||||
sToken.append(1, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Any residual characters at this point just make up the final token,
|
||||
// so clean it up and add it to the vector of values
|
||||
if (!sToken.empty())
|
||||
bEscapeChar=false;
|
||||
// Is character a quote...
|
||||
if (c == '\"')
|
||||
{
|
||||
trim(sToken);
|
||||
stkPath.top().get()[sPropName].SetString(sToken, nTokenCount);
|
||||
// ...yes, so toggle quote state
|
||||
bInQuotes = !bInQuotes;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...but if it doesnt, then it's something structural
|
||||
if (line[0] == '{')
|
||||
else
|
||||
if (c == '\\')
|
||||
{
|
||||
// Open brace, so push this node to stack, subsequent properties
|
||||
// will belong to the new node
|
||||
stkPath.push(stkPath.top().get()[sPropName]);
|
||||
// ...yes, so toggle quote state
|
||||
bEscapeChar=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line[0] == '}')
|
||||
// ...no, so proceed creating token. If we are in quote state
|
||||
// then just append characters until we exit quote state.
|
||||
if (bInQuotes)
|
||||
{
|
||||
// Close brace, so this node has been defined, pop it from the
|
||||
// stack
|
||||
stkPath.pop();
|
||||
sToken.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Line is a property with no assignment. Who knows whether this is useful,
|
||||
// but we can simply add it as a valueless property...
|
||||
sPropName = line;
|
||||
// ...actually it is useful, as valuless properties are typically
|
||||
// going to be the names of new datafile nodes on the next iteration
|
||||
// Is the character our seperator? If it is
|
||||
if (c == sListSep)
|
||||
{
|
||||
// Clean up the token
|
||||
trim(sToken);
|
||||
// Add it to the vector of values for this property
|
||||
stkPath.top().get()[sPropName].SetString(sToken, nTokenCount);
|
||||
// Reset our token state
|
||||
sToken.clear();
|
||||
nTokenCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It isnt, so just append to token
|
||||
sToken.append(1, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any residual characters at this point just make up the final token,
|
||||
// so clean it up and add it to the vector of values
|
||||
if (!sToken.empty())
|
||||
{
|
||||
trim(sToken);
|
||||
stkPath.top().get()[sPropName].SetString(sToken, nTokenCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...but if it doesnt, then it's something structural
|
||||
if (line[0] == '{')
|
||||
{
|
||||
// Open brace, so push this node to stack, subsequent properties
|
||||
// will belong to the new node
|
||||
stkPath.push(stkPath.top().get()[sPropName]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line[0] == '}')
|
||||
{
|
||||
// Close brace, so this node has been defined, pop it from the
|
||||
// stack
|
||||
stkPath.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Line is a property with no assignment. Who knows whether this is useful,
|
||||
// but we can simply add it as a valueless property...
|
||||
sPropName = line;
|
||||
// ...actually it is useful, as valuless properties are typically
|
||||
// going to be the names of new datafile nodes on the next iteration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close and exit!
|
||||
file.close();
|
||||
INITIAL_SETUP_COMPLETE=previousSetupState;
|
||||
return true;
|
||||
}
|
||||
|
||||
// File not found, so fail
|
||||
ERR("WARNING! Could not open file "<<sFileName<<"!");
|
||||
INITIAL_SETUP_COMPLETE=previousSetupState;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user