Sign in to follow this  

Moving from plain "ascii" to XML?

This topic is 413 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,
I'm planning to move from reading ascii/ plain text from files, to XML.
To explain myself better, my 3d engine reads data from files, just an example:

File: settings.dat
Contents:
#XRESOLUTION 1280
#YRESOLUTION 720
#WINDOWED false
Etc.

In my codebase I simply use an fstream and read out the values linearly through the file, checking the labels.
Now I want to change this to XML, example:

<d3d11_settings>
<xresolution>1280</xresolution>
<yresolution>720<yresolution>
<windowed>true</windowed>
</d3d11_settings>

My questions are:
- what tools can I use to generate/ manually create the XSD? (defining field names, minoccurs/maxoccurs, data type etc.)
- assuming I have the XSD, what (parser) library do you advice?

I'm trying to keep things simple, for example the library should simply return me the xresolution value when I request it (from the loaded XML file).
Any input is appreciated.

Share this post


Link to post
Share on other sites

I'm planning to move from reading ascii/ plain text from files, to XML.
...
I'm trying to keep things simple,

 

You have simple.  Moving to XML buys you what exactly?  Bloated and less readable data files?  Slower parsing?  Dependencies on third party XML parsers?

Share this post


Link to post
Share on other sites

http://yaml.org/ , in particular, yaml-cpp. From their example/tutorial:

YAML::Node config = YAML::LoadFile("config.yaml");

if (config["lastLogin"]) {
  std::cout << "Last logged in: " << config["lastLogin"].as<DateTime>() << "\n";
}

const std::string username = config["username"].as<std::string>();
const std::string password = config["password"].as<std::string>();
login(username, password);
config["lastLogin"] = getCurrentDateTime();

std::ofstream fout("config.yaml");
fout << config;

Share this post


Link to post
Share on other sites

As has been said before the current format is already much simpler than XML... I wouldn't bother with XML unless you end up with complex enough structures to warrant deep trees. Doesn't look like that's the case here.

 

If you really want to switch to something more standard, I guess you could go with INI files, though there's a bunch of libraries out there to look for... and it may even be feasible to just write your own parser (the main reason they took off back then is that they're that simple, after all). Or even modify your own, I mean, replace the space with an equals sign and you're halfway there =P

Share this post


Link to post
Share on other sites

Note that parsers which respect XSDs are about 10x larger libraries and much slower, too.

 

And on this point, note that you probably don't need the functionality that XSD provides at runtime in any case. XSD is useful for validation, and for authoring content in aware text editors, and for enabling XSLT transformations, but you don't need any of that after release. Your XML files will already have been authored and transformed and ought already have been validated by the time you ship them to customers.

Share this post


Link to post
Share on other sites

Lets see, verbose XML:

<d3d11_settings>
  <xresolution>1280</xresolution>
  <yresolution>720<yresolution>
  <windowed>true</windowed>
</d3d11_settings>

If you're doing XML you should probably do it like:

<d3d11_settings
 xresolution="1280"
 yresolution="720"
 windowed="true" />

ie, use attributes.

 

If you're doing JSON:

"d3d11_settings": {
  "xresolution": 1280,
  "yresolution": 720,
  "windowed": true
}

ie, nicer.

 

If you're doing YAML:

d3d11_settings:
  xresolution: 1280
  yresolution: 720
  windowed: true

ie, "look ma! no double quotes!".

 

I'd use something else than XML, YAML is nice. JSON is not as nice, but its much more widely used (thus more info on it, more libs, etc). 

 

EDIT: Fixed the JSON as fastcall mentions below.

 

Anyway, this is my YAML window stuff as an example:

width: 1280
height: 720
fullscreen: false
vsync: yes
softsync: false
vfov: 75
renderdist: 256
resizable: yes

Basically using it as a normal key-value thingy, except i can just call "load" and I get a Map<String,Object> out of it. My renderer pipeline is also made in YAML, using the compact notation, here is a render pass:

dirlight_pass: &dirlight_pass
  name: dirlight_pass
  program: df_dir_light
  drawing:
    name: gbuffer
    targets: [light_acc]
  sampling:
    name: gbuffer
    targets: [albedo, normal, misc, depth]
  samplers:
    - [default, default, default, default]
  batch: LIGHT_DIR
  updaters: [DIRLIGHT]
  descriptors:
    writing: COLOR_ONLY
    depth:
      func: GREATER
      range: [1.0, 1.0]
      testing: YES
      clamping: NO
    scissor: DEFAULT
    viewport: DEFAULT
    stencil: NO
    triangle: DEFAULT
    blending: NO
    clearing: COLOR_ONLY
Edited by TheChubu

Share this post


Link to post
Share on other sites
Chubu’s JSON example is not quite correct. There are other datatypes besides string in JSON:
{
    "d3d11_settings": {
        "xresolution": 1280,
        "yresolution" 720,
        "windowed": true
    }
}
Though, I would probably combine the resolution properties into an array:
[tt]
"resolution": [1280,720],
[/tt]


Call me old fashioned, but a simple `key=value` with `#comments` is dead simple and suitable for most configuration. INI, perhaps... Edited by fastcall22

Share this post


Link to post
Share on other sites
Another option would be using Lua. It's a full blown scripting language, that actually started out as being used for config files.

If you intend to add scripting support to your library as well this might be a good match.

Share this post


Link to post
Share on other sites

Thanks guys, this helps.

What I basically conclude from this, is that I shouldn't make it too complex.

 

I'll go for my own format where I can 'learn' from other formats. With this I'll have to find a way to parse efficiently and reducing the chance of reading the wrong data in the wrong fields. The thing I ran into quite some times (in my last engine), is that I forgot one of the fields for some purpose, and then all data from there was messed up. This can probably be solved by better parsing and more checks.

 

Would you make an overall parser for your own format, for all possible variants of data?

Or just per type of data, for example, this is what I have now for loading the settings:

/**************************************************************************************/
/***									LOAD 										***/
/*** ==> usage: to load a configuration file with settings  						***/
/*** ==> updates all settings from the Crealysm IO config file 						***/
/**************************************************************************************/

bool CD3dSettings::Load(const std::string &pFilename)
{
	std::ifstream loadSettings(pFilename);
	if(!loadSettings) return false;

	mFilename = pFilename;

	char temp[255];

	loadSettings >> temp;
	if(0 != strcmp(temp, "#SCREEN_WIDTH"))		return false;
	loadSettings >> mScreenWidth;

	loadSettings >> temp;
	if(0 != strcmp(temp, "#SCREEN_HEIGHT"))		return false;
	loadSettings >> mScreenHeight;

	loadSettings >> temp;
	if(0 != strcmp(temp, "#WINDOWED"))			return false;
	
	loadSettings >> temp;
	if(0 == strcmp(temp, "true"))		mWindowed = true;
	else if(0 == strcmp(temp, "false"))	mWindowed = false;
	else return false;

	loadSettings >> temp;
	if(0 != strcmp(temp, "#NR_MSAA"))			return false;
	
	loadSettings >> mMSAANrSamples;
	if(mMSAANrSamples != 0 && mMSAANrSamples != 2 && mMSAANrSamples != 4 && mMSAANrSamples != 8 && mMSAANrSamples != 16)
	{		
		#ifdef _DEBUG
		OutputDebugString(L"CREALYSM: Invalid number of MSAA samples in D3D settings!\n");
		#endif
		return false;
	}
	if(mMSAANrSamples == 0) mMSAAEnabled = false;

	loadSettings >> temp;
	if(0 != strcmp(temp, "#V_SYNC"))			return false;
	
	loadSettings >> temp;
	if(0 == strcmp(temp, "true"))		mVsync = true;
	else if(0 == strcmp(temp, "false"))	mVsync = false;
	else return false;
	
	loadSettings >> temp;
	if(0 != strcmp(temp, "#ANISOTROPIC"))		return false;
	
	loadSettings >> temp;
	if(0 == strcmp(temp, "true"))		mAnisotropy = true;
	else if(0 == strcmp(temp, "false"))	mAnisotropy = false;
	else return false;

	loadSettings >> temp;
	if(0 != strcmp(temp, "#ANISOTROPIC_LEVEL"))	return false;
	loadSettings >> mAnisotropyLevel;

	loadSettings.close();

	// IF WINDOWED, SET WINDOW WIDTH & HEIGHT
	if(mWindowed)
	{
		mWindowWidth = mScreenWidth;
		mWindowHeight = mScreenHeight;
	}
	return true;
}

With a txt file like this:

#SCREEN_WIDTH		1280
#SCREEN_HEIGHT		720
#WINDOWED		true
#NR_MSAA		8
#V_SYNC			true
#ANISOTROPIC		true
#ANISOTROPIC_LEVEL	4

Share this post


Link to post
Share on other sites

Wait, that parser can only accept the options if they come in that exact order? (even though each line starts with the name of the relevant setting)

As a counterpoint though, it can be convenient for the end user if config files ARE human-readable.

Yeah, especially if you changed something that keeps crashing the game miserably and want to undo it. Being able to edit the settings directly makes this task easier. It's also a good place to put obscure settings that normally wouldn't be useful (i.e. extremely advanced stuff) but that somebody may want to give a try.

Share this post


Link to post
Share on other sites

Currently it does, I could make some 'general' parser where the found label is recognized and that settings is filled.

But I'm not sure if that's worth the effort, because the settings are Always in this order, when the user changes resolution, windowed mode etc, the settings are stored in the exact same order.

Also I wonder if a generic parser works when there are quite some different properties in the files.

Here's just another example:

<SHADER_EFFECT_0>
#VS		VS_PS_basic.hlsl	VS_main
#PS		VS_PS_basic.hlsl	PS_main
#GS		no
#DEFINES	MAX_PLIGHT_8;NORMALMAP;;
#IDENTIFIER	8PTLIGHT-NORMALMAP

Share this post


Link to post
Share on other sites

 

I'll go for my own format where I can 'learn' from other formats.
*sigh* There are tons of libs that you can literally call 'load(path)' and it returns a key-value structure. YAML/JSON/XML schemas are as complex or simple as you want them to be, they're very flexible. You already spent more time in coding that piece of code than in what it takes to download one and link it to your project, "I shouldn't make it complex" is the opposite of rolling your own. 

 

 

This ^^^

Look at ini file parsers. They sound like they'll do what you're looking for: https://www.google.com/#q=simple+ini+file+parser+c%2B%2B

Share this post


Link to post
Share on other sites

Lets see, verbose XML: 1280720true If you're doing XML you should probably do it like: ie, use attributes.

This is one of the main reasons why I personally consider XML "bad".

 

There are different, competing ways of doing the same thing, but it is not the same thing (although you are led to believe that). They are not compatible logic-wise and library-API wise. And... it's easy to fuck it all up because it's not immediately obvious to the novice.

 

Also, even for someone more experienced, it's entirely possible to get it wrong by accident when writing the XML by hand late at night. Then you just notice that things don't work as expected, and you wonder why. Because you know... everything is there, just why doesn't the program pick it up.

 

(Yes of course, this is one thing that you can address with a XSD... cough... but, I mean...  simple?)

 

The mentioned alternatives don't have that ambiguity to begin with.

Share this post


Link to post
Share on other sites

Add me to the "just use JSON" list. XML is bloated and too many people use tags when they should use attributes. INI files get awkward as soon as you start needing any more complex or hierarchical data. And hand-rolling your own parser for your own format is a colossal waste of time in 2016.

Share this post


Link to post
Share on other sites

Hi all.

I've developed the INI parser like I said, with the good news that I just finished debugging and testing (not 100% the INI standard, but ini'ish :))

If you're interested and/or have any thoughts, below the source code.

 

It's not 100% bulletproof, but this is what it does for sure:

- both reading and parsing INI files as updating a specific attribute

- works with blocks and attributes, a block starts with [ ], followed by lines with attributes

- supports multiple blocks with the same name and same attributes, for example for ShaderPacks (see example below)

- it's lightweight and only needs std::string

 

Thanks for your thoughts on this topic and till next time.

 

Header:

/*** INI parser, Cristiaan Kop								 	 updated 25-10-2016 ***/
/*** ============================================================================== ***/
/*** NAMESPACE: Crealysm::COMMON													***/
/*** header file for the CIniParser class interface					     		    ***/

#ifndef CINI_PARSER_H
#define CINI_PARSER_H

#include <string>
#include <fstream>
#include <vector>


namespace Crealysm { namespace COMMON
{

/**************************************************************************************/
/***								CIniParser class								***/
/*** class for reading/updating INI files and retrieving data from them: parsing 	***/
/**************************************************************************************/

class CIniParser
{
public:
	CIniParser();
	~CIniParser();

	/***** START AND STOP READING *****/
	bool StartReading(const std::string &pFilename);
	bool StopReading();

	/***** SET THE CURRENT FILENAME FOR WRITING *****/
	void SetWriteFile(const std::string &pFilename);

	// Reading: moves filestream to a requested or the next block
	bool GetNextBlockType(uint *pNrAttributes, std::string *pBlockName);
	bool GotoBlockByName(const std::string &pBlockName, const uint pBlockId, uint *pNrAttributes);
		
	// Reading: retrieve next attribute's name
	std::string GetNextAttributeName();

	// Reading: retrieve attribute's value
	int				GetInt();
	bool			GetBool();
	std::string		GetString();

	/***** WRITING *****/
	bool UpdateInt(const std::string &pBlockName, const uint pBlockId, const std::string &pAttrib, const int pIntVal);
	bool UpdateBool(const std::string &pBlockname, const uint pBlockId, const std::string &pAttrib, const bool pBoolVal);
	bool UpdateString(const std::string &pBlockName, const uint pBlockId, const std::string &pAttrib, const std::string pStringVal);

private:
	std::ifstream mReadIni;
	
	std::ofstream mWriteIni;
	std::vector<std::string> mTempReadLines;
	std::string mWriteFile;

	// Helper functions
	bool GetBlockName(const std::string &pLine, std::string *pBlockName)	const;

	bool IsBlockLine(const std::string &pLine)			const;
	bool IsCommentLine(const std::string &pLine)		const; 
	bool IsEmptyLine(const std::string &pLine)			const;

	int CountAttributes();

	/***** WRITING *****/
	bool PrepareUpdate(const std::string &pBlockName, const uint pBlockId, const std::string &pAttrib, uint *pStopLine);
	bool FinishUpdate(const uint pContinueLine);
};

}
}

#endif

Implementation:

/*** INI parser, Cristiaan Kop						  		 	 updated 25-10-2016 ***/
/*** ============================================================================== ***/
/*** NAMESPACE: Crealysm::COMMON													***/
/*** cpp file with CIniPasser class implementation									***/

#include "ciniparser.h"

using namespace std;


namespace Crealysm { namespace COMMON
{

/**************************************************************************************/
/***								CONSTRUCTOR										***/
/*** ==> usage: when creating a CIniParser object 									***/
/*** ==> sets all variables in the class object to initial/ passed parameter values	***/
/**************************************************************************************/

CIniParser::CIniParser() 
{
	// nothing yet
}

/**************************************************************************************/
/***								DESTRUCTOR										***/
/*** ==> usage: when CIniParser object is not needed anymore						***/
/*** ==> releases stuff, COM objects, memory etc. 									***/
/**************************************************************************************/

CIniParser::~CIniParser()
{
	if(mReadIni.is_open()) mReadIni.close();
	if(mWriteIni.is_open()) mWriteIni.close();
}

/**************************************************************************************/
/***								START READING							  		***/
/*** ==> usage: to open the file stream for reading the INI file 					***/
/*** ==> opens the fstream, based on input filename									***/
/**************************************************************************************/

bool CIniParser::StartReading(const string &pFilename)
{
	if(mReadIni.is_open()) mReadIni.close();

	mReadIni.open(pFilename);
	if(!mReadIni) return false;

	return true;
}

/**************************************************************************************/
/***								STOP READING							  		***/
/*** ==> usage: to close the file stream after reading the INI file 				***/
/*** ==> closes the fstream, if open												***/
/**************************************************************************************/

bool CIniParser::StopReading()
{
	if(mReadIni.is_open())
	{
		mReadIni.close();
		return true;
	}
	return false;
}

/**************************************************************************************/
/***								SET WRITEFILE							  		***/
/*** ==> usage: to set the filename for writing					 					***/
/*** ==> only updates the member string with the writefile filename 				***/
/**************************************************************************************/

void CIniParser::SetWriteFile(const string &pFilename)
{
	mWriteFile = pFilename;
}

/**************************************************************************************/
/***								GET NEXT BLOCKTYPE								***/
/*** ==> usage: to retrieve the next blocktype and nr of attributes in the INI file	***/
/*** ==> returns the name and nr of attributes of the 1st following block 			***/
/**************************************************************************************/

bool CIniParser::GetNextBlockType(uint *pNrAttributes, std::string *pBlockName)
{
	if(!mReadIni.is_open()) return false;

	// determine block type
	string line = "";
	while(!IsBlockLine(line))		// no comment or empty line
	{
		getline(mReadIni, line);
	}
	

	// valid block type found?
	if(!GetBlockName(line, pBlockName)) return false;

	// count the number of attributes
	*pNrAttributes = CountAttributes();

	return true;
}

/**************************************************************************************/
/***								GOTO BLOCK BY NAME								***/
/*** ==> usage: directly move stream to requested block, supports identical blocks	***/
/*** ==> moves the filestream if the block exists, otherwise keeps its position 	***/
/**************************************************************************************/

bool CIniParser::GotoBlockByName(const std::string &pBlockName, const uint pBlockId, uint *pNrAttributes)
{
	if(!mReadIni.is_open()) return false;

	// store original position, return if requested block not found
	int orgPos = (int)mReadIni.tellg();
	
	// go to beginning of filestream
	mReadIni.seekg(0, ios::beg);

	string line;
	int currBlockFound = -1;

	while(!mReadIni.eof())
	{
		getline(mReadIni, line);

		if(IsBlockLine(line))
		{
			string foundBlock;
			GetBlockName(line, &foundBlock);
			if(strcmp(foundBlock.c_str(), pBlockName.c_str()) == 0)
			{
				++currBlockFound;
				if(currBlockFound == pBlockId)
				{
					*pNrAttributes = CountAttributes();
					return true;
				}
			}
		}
	}
	
	// restore stream position if not found
	mReadIni.seekg(orgPos);
	return false;
}

/**************************************************************************************/
/***							GET NEXT ATTRIBUTE NAME								***/
/*** ==> usage: to retrieve the label/ name of the next attribute			 		***/
/*** ==> returns a string with the attribute's name									***/
/**************************************************************************************/

string CIniParser::GetNextAttributeName()
{
	if(!mReadIni.is_open()) return "";		// empty, file is not open

	int currPos = 0;
	string tempString;

	while(true)
	{
		currPos = (int)mReadIni.tellg();
		getline(mReadIni, tempString);
		if(!IsCommentLine(tempString) && !IsEmptyLine(tempString) && !IsBlockLine(tempString)) break;		// no comment or empty line
	}

	mReadIni.seekg(currPos);	// reset position in filestream, before retrieving the value

	string result, tseparator;
	mReadIni >> result;
	mReadIni >> tseparator;		// skip the '='
	
	return result;
}

/**************************************************************************************/
/***									GET INT										***/
/*** ==> usage: to retrieve the attribute´s value: INTEGER					 		***/
/*** ==> returns the read value from the filestream, as int							***/
/**************************************************************************************/

int CIniParser::GetInt()
{
	if(!mReadIni.is_open()) return -1;

	int result;
	mReadIni >> result;
	return result;
}

/**************************************************************************************/
/***									GET BOOL									***/
/*** ==> usage: to retrieve the attribute´s value: BOOLEAN					 		***/
/*** ==> returns the read value from the filestream, as bool						***/
/**************************************************************************************/

bool CIniParser::GetBool()
{
	if(!mReadIni.is_open()) return false;

	std::string tempResult;
	mReadIni >> tempResult;

	if(strcmp(tempResult.c_str(), "true") == 0) return true;
	if(strcmp(tempResult.c_str(), "false") == 0) return false;

	return false;
}

/**************************************************************************************/
/***									GET STRING									***/
/*** ==> usage: to retrieve the attribute´s value: STRING					 		***/
/*** ==> returns the read value from the filestream, as std::string					***/
/**************************************************************************************/

string CIniParser::GetString()
{
	if(!mReadIni.is_open()) return "";

	string result;
	mReadIni >> result;
	return result;
}

/**************************************************************************************/
/***							COUNT ATTRIBUTES									***/
/*** ==> usage: count number of attributes from current pos till next block			***/
/*** ==> stores stream pos, counts till next block and restores stream pos 			***/
/**************************************************************************************/

int CIniParser::CountAttributes()
{
	if(!mReadIni.is_open()) return -1;
	
	string line;
	bool nextBlock = false;
	int nrAttributes = 0;

	uint currPos = (int)mReadIni.tellg();	// store position in stream before counting

	while(!nextBlock)
	{
		getline(mReadIni, line);
		if(!IsEmptyLine(line) && !IsCommentLine(line))		// ignore empty lines and comments
		{
			if(IsBlockLine(line))
			{
				nextBlock = true;
				break;
			}
			else ++nrAttributes;
		}
	}
	// restore position in the file
	mReadIni.seekg(currPos);

	return nrAttributes;
}

/**************************************************************************************/
/***									UPDATE INT									***/
/*** ==> usage: to update a value of an attribute within a given block: INTEGER		***/
/*** ==> seeks the attribute's value in the stream and updates it 					***/
/**************************************************************************************/

bool CIniParser::UpdateInt(const std::string &pBlockName, const uint pBlockId, const std::string &pAttrib, const int pIntVal)
{
	uint attribLine = 0;
	if(!PrepareUpdate(pBlockName, pBlockId, pAttrib, &attribLine)) return false;

	mWriteIni << pAttrib << " = " << pIntVal << endl;

	if(!FinishUpdate(attribLine+1)) return false;
	return true;
}

/**************************************************************************************/
/***									UPDATE BOOL									***/
/*** ==> usage: to update a value of an attribute within a given block: BOOL		***/
/*** ==> seeks the attribute's value in the stream and updates it 					***/
/**************************************************************************************/

bool CIniParser::UpdateBool(const std::string &pBlockName, const uint pBlockId, const std::string &pAttrib, const bool pBoolVal)
{
	uint attribLine = 0;
	if(!PrepareUpdate(pBlockName, pBlockId, pAttrib, &attribLine)) return false;

	mWriteIni << pAttrib << " = ";
	if(pBoolVal) mWriteIni << "true" << endl;
	else mWriteIni << "false" << endl;

	if(!FinishUpdate(attribLine+1)) return false;
	return true;
}

/**************************************************************************************/
/***									UPDATE STRING								***/
/*** ==> usage: to update a value of an attribute within a given block: STRING		***/
/*** ==> seeks the attribute's value in the stream and updates it 					***/
/**************************************************************************************/

bool CIniParser::UpdateString(const std::string &pBlockName, const uint pBlockId, const std::string &pAttrib, const std::string pStringVal)
{
	uint attribLine = 0;
	if(!PrepareUpdate(pBlockName, pBlockId, pAttrib, &attribLine)) return false;

	mWriteIni << pAttrib << " = " << pStringVal << endl;

	if(!FinishUpdate(attribLine+1)) return false;
	return true;
}

/**************************************************************************************/
/***								PREPARE UPDATE									***/
/*** ==> usage: to prepare the updating of an attribute value						***/
/*** ==> stores the file in a vector of strings and seeks the to be updated attr	***/
/**************************************************************************************/

bool CIniParser::PrepareUpdate(const std::string &pBlockName, const uint pBlockId, const std::string &pAttrib, uint *pStopLine)
{
	if(mWriteIni.is_open()) return false;

	ifstream tempRead(mWriteFile);
	if(!tempRead) return false;

	// First store whole file as strings (per line)
	mTempReadLines.resize(0);
	
	while(!tempRead.eof())
	{
		string tempLine;
		getline(tempRead, tempLine);

		mTempReadLines.push_back(tempLine);
	}
	tempRead.close();

	// Find the the correct block
	int blockIdFound = -1;
	int blockLine = 0;

	for(uint bc=0;bc<mTempReadLines.size();++bc)
	{
		if(IsBlockLine(mTempReadLines[bc]))
		{
			string tblock;
			if(!GetBlockName(mTempReadLines[bc], &tblock)) return false;
			if(strcmp(tblock.c_str(), pBlockName.c_str()) == 0) ++blockIdFound;
		}
		if(blockIdFound == pBlockId)
		{
			blockLine = bc;					// right block found
			break;
		}
	}

	// Find the correct attribute
	int attribLine = 0;
	for(uint lc=blockLine+1;lc<mTempReadLines.size();++lc)
	{
		if(IsBlockLine(mTempReadLines[lc])) break;

		if(!IsCommentLine(mTempReadLines[lc]) && !IsEmptyLine(mTempReadLines[lc]))
		{
			if(mTempReadLines[lc].find(pAttrib.c_str()) != string::npos)
			{
				attribLine = lc;			// attribute found
				break;
			}
		}
	}
	
	// Found it at all?
	if(blockLine == 0 || attribLine == 0) return false;

	// Rewrite the file, including the changed attribute
	mWriteIni.open(mWriteFile);
	for(int w=0;w<attribLine;++w) mWriteIni << mTempReadLines[w] << endl;
	
	*pStopLine = attribLine;
	return true;
}

/**************************************************************************************/
/***								FINISH UPDATE									***/
/*** ==> usage: to finish the updating of an attribute value						***/
/*** ==> saves the file from after the updated attribute							***/
/**************************************************************************************/

bool CIniParser::FinishUpdate(const uint pContinueLine)
{
	if(!mWriteIni.is_open()) return false;

	for(uint w=pContinueLine;w<mTempReadLines.size();++w)
	{
		mWriteIni << mTempReadLines[w] << endl;
	}
	mWriteIni.close();
	return true;
}




/**************************************************************************************/
/***								GET BLOCKNAME							  CONST	***/
/*** ==> usage: to retrieve the name of a block, from a blockline string	 		***/
/*** ==> returns the part of the string between [ ]									***/
/**************************************************************************************/

bool CIniParser::GetBlockName(const std::string &pLine, std::string *pBlockName) const
{
	if(!IsBlockLine(pLine)) return false;
	*pBlockName = pLine.substr(1, pLine.length()-2);

	return true;
}

/**************************************************************************************/
/***								IS BLOCKLINE							 CONST	***/
/*** ==> usage: to check if a given line is defined as a 'block' definition  		***/
/*** ==> returns true if it's a block line (starts and ends with [ ])				***/
/**************************************************************************************/

bool CIniParser::IsBlockLine(const string &pLine) const
{
	if(IsEmptyLine(pLine)) return false;

	if(pLine.at(0) == '[' && pLine.at(pLine.length()-1) == ']')	return true;

	return false;
}

/**************************************************************************************/
/***								IS COMMENTLINE							 CONST	***/
/*** ==> usage: to check if a given line is a comment line (starts with ; )  		***/
/*** ==> returns true if it's a comment line										***/
/**************************************************************************************/

bool CIniParser::IsCommentLine(const string &pLine) const 
{
	if(pLine.length() < 1) return false;

	if(pLine.at(0) == ';')	return true;
	return false;
}

/**************************************************************************************/
/***								IS EMPTYLINE							 CONST	***/
/*** ==> usage: to check if a given line is a empty line					  		***/
/*** ==> returns true if the string length's 0										***/
/**************************************************************************************/

bool CIniParser::IsEmptyLine(const string &pLine) const
{
	if(pLine.length() < 1) return true;
	return false;
}

}
}

Tests:

	// TESTING
	int twidth = 0;
	int theight = 0;
	bool twindowed = false;
	int tmsaa = 8;
	bool tvsync = false;
	bool taniso = false;
	int tanisolevel = -1;
	
	Crealysm::COMMON::CIniParser myParser;
	
	/* READ TEST 1
	std::string blockName;
	uint nrAttrib = 0;

	myParser.StartReading("data/settings.ini");

	if(!myParser.GetNextBlockType(&nrAttrib, &blockName)) return false;
	if(strcmp(blockName.c_str(), "d3dsettings") == 0)
	{
		// read attributes
	}
	*/

	// READ TEST 2
	myParser.StartReading("data/settings.ini");
	uint nrAttrib = 0;
	
	if(!myParser.GotoBlockByName("d3dsettings", 1, &nrAttrib)) return false;

	for(uint attr=0;attr<nrAttrib;++attr)
	{
		std::string attribute = myParser.GetNextAttributeName();
		if(attribute == "screen_width") twidth = myParser.GetInt();
		if(attribute == "screen_height") theight = myParser.GetInt();
		if(attribute == "windowed") twindowed = myParser.GetBool();
		if(attribute == "nr_msaa") tmsaa = myParser.GetInt();
		if(attribute == "v_sync") tvsync = myParser.GetBool();
		if(attribute == "anisotropic") taniso = myParser.GetBool();
		if(attribute == "anisotropic_level") tanisolevel = myParser.GetInt();
	}
	myParser.StopReading();

	std::ofstream debugger("results.txt");
	debugger << "width: " << twidth << std::endl;
	debugger << "height: " << theight << std::endl;
	debugger << "windowed: " << twindowed << std::endl;
	debugger << "msaa: " << tmsaa << std::endl;
	debugger << "vsync: " << tvsync << std::endl;
	debugger << "aniso: " << taniso << std::endl;
	debugger << "anisolevel: " << tanisolevel << std::endl;
	debugger.close();
	
	// WRITE TEST
	//myParser.SetWriteFile("data/settings.ini");
	//if(!myParser.UpdateInt("d3dsettings", 0, "msaa", 4)) return false;
	//if(!myParser.UpdateBool("d3dsettings", 1, "windowed", true)) return false;
	//if(!myParser.UpdateString("newblock", 0, "myname", "earl")) return false;

Tested INI files:

[the_other_block]
attrib1 = 230
attrib3 = false

[blocktesting]
; comments
[d3dsettings]
; d3d11 settings for the Crealysm11 3d engine
screen_width = 1280
screen_height = 720
windowed = true
msaa = 4
; more comments to mess up stuff
v_sync = true
anisotropic = true
anisotropic_level = 4

; even more comment
; ini file format for trying out

[newblock]
myname = cris

[multi:shaderpacks]
[ShaderPack]
vs_filename = VS_PS_basic.hlsl
vs_entrypoint = VS_main
ps_filename = VS_PS_basic.hlsl
ps_entrypoint = PS_main
gs_filename = no
gs_entrypoint = no
defines = MAX_PLIGHT_8;NORMALMAP
identifier = 8PTLIGHT-NORMALMAP

[ShaderPack]
vs_filename = VS_PS_basic.hlsl
vs_entrypoint = VS_main
ps_filename = VS_PS_basic.hlsl
ps_entrypoint = PS_main
gs_filename = no
gs_entrypoint = no
defines = MAX_PLIGHT_4
identifier = 4PTLIGHT

[ShaderPack]
vs_filename = VS_thingie.hlsl
vs_entrypoint = VS_main
ps_filename = no
ps_entrypoint = no
gs_filename = no
gs_entrypoint = no
defines = no
identifier = SPECIAL-VS-ONLY

[ShaderPack]
vs_filename = VSGS_special.hlsl
vs_entrypoint = VS_main
ps_filename = no
ps_entrypoint = no
gs_filename = VSGS_special.hlsl
gs_entrypoint = GS_dostuff
defines = no
identifier = SPECIAL-VSGS

Share this post


Link to post
Share on other sites

This topic is 413 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this