Перейти к контенту

[SOC] Мелкие правки движка


Kondr48

Рекомендуемые сообщения

Подкину еще немножко своих правок, для движка OGSR
 

Скрытый текст


 

Описание: в качестве имен текстур, движок понимает в т.ч. секции конфига. В этом случае, в качестве текстуры будет использована иконка соответствующего предмета.

xrGame/ui/uiTextureMaster.h


// file:		UITextureMaster.h
// description:	holds info about shared textures. able to initialize external
//				through IUITextureControl interface
// created:		11.05.2005
// author:		Serge Vynnychenko
// mail:		narrator@gsc-game.kiev.ua
//
// copyright 2005 GSC Game World

#pragma once

#include "ui_defs.h"

class IUISimpleTextureControl;

struct TEX_INFO{
	shared_str	file;
	Frect		rect;
	LPCSTR		get_file_name	()	{return *file;}
	Frect		get_rect		()	{return rect;}
};

class CUITextureMaster{
public:

	static void ParseShTexInfo			(LPCSTR xml_file);
	static void ParseConfigIcon			(LPCSTR section);
	static LPCSTR CheckName(LPCSTR texture_name);

	static void		InitTexture			(LPCSTR texture_name,		IUISimpleTextureControl* tc);
	static void		InitTexture			(LPCSTR texture_name, const char* shader_name, IUISimpleTextureControl* tc);
	static float	GetTextureHeight	(LPCSTR texture_name);
	static float	GetTextureWidth		(LPCSTR texture_name);
	static Frect	GetTextureRect		(LPCSTR texture_name);
	static LPCSTR	GetTextureFileName	(LPCSTR texture_name);
	static void		GetTextureShader	(LPCSTR texture_name, ui_shader& sh);
	static TEX_INFO	FindItem			(LPCSTR texture_name, LPCSTR def_texture_name);
	static void WriteLog();
	static bool		CheckTextureExist	(LPCSTR texture_name);

protected:
	IC	static bool IsSh					(const char* texture_name);



//	typedef xr_string region_name;
//	typedef xr_string shader_name;
//	typedef xr_map<region_name, Frect>				regions;
//	typedef xr_map<region_name, Frect>::iterator	regions_it;
//	typedef xr_map<shader_name, regions>			shared_textures;
//	typedef xr_map<shader_name, regions>::iterator	shared_textures_it;

	static xr_map<shared_str, TEX_INFO>					m_textures;

//	static	shared_textures		m_shTex;
#ifdef DEBUG
	static u32		m_time;
#endif 
};

 

xrGame/ui/uiTextureMaster.cpp


// file:		UITextureMaster.h
// description:	holds info about shared textures. able to initialize external controls
//				through IUITextureControl interface
// created:		11.05.2005
// author:		Serge Vynnychenko
// mail:		narrator@gsc-game.kiev.ua
//
// copyright 2005 GSC Game World


#include "StdAfx.h"
#include "UITextureMaster.h"
#include "uiabstract.h"
#include "xrUIXmlParser.h"

xr_map<shared_str, TEX_INFO>	CUITextureMaster::m_textures;
#ifdef DEBUG
u32									CUITextureMaster::m_time = 0;
#endif

void CUITextureMaster::WriteLog(){
#ifdef DEBUG
	Msg("UI texture manager work time is %d ms", m_time);
#endif
}
void CUITextureMaster::ParseShTexInfo(LPCSTR xml_file){
	CUIXml xml;
	xml.Init(CONFIG_PATH, UI_PATH, xml_file);
	shared_str file = xml.Read("file_name",0,""); 

//	shared_textures_it	sht_it = m_shTex.find(texture);
//	if (m_shTex.end() == sht_it)
//	{
		int num = xml.GetNodesNum("",0,"texture");
//		regions regs;
		for (int i = 0; i<num; i++)
		{
			TEX_INFO info;

			info.file = file;

			info.rect.x1 = xml.ReadAttribFlt("texture",i,"x");
			info.rect.x2 = xml.ReadAttribFlt("texture",i,"width") + info.rect.x1;
			info.rect.y1 = xml.ReadAttribFlt("texture",i,"y");
			info.rect.y2 = xml.ReadAttribFlt("texture",i,"height") + info.rect.y1;
			shared_str id = xml.ReadAttrib("texture",i,"id");

			m_textures.insert(mk_pair(id,info));
		}
//		m_shTex.insert(mk_pair(texture, regs));
//	}
}

LPCSTR CUITextureMaster::CheckName(const char* texture_name){
	LPCSTR r;
	if (pSettings->section_exist(texture_name))
	{
		shared_str subsection = pSettings->r_string(texture_name, "icon_section", texture_name);
		subsection = pSettings->r_string(subsection, "used_icon_texture", subsection.c_str());
		r = subsection.c_str();
	}
	else {
		r = texture_name;
	}
	if (!CheckTextureExist(r)) ParseConfigIcon(r);
	if (!CheckTextureExist(r)) r = texture_name;
	return r;
}

void CUITextureMaster::ParseConfigIcon(LPCSTR section){
	if (pSettings->section_exist(section))
	{
		shared_str subsection = pSettings->r_string(section, "icon_section", section);
		subsection = pSettings->r_string(subsection, "used_icon_texture", subsection);
		int place_id = pSettings->r_float(subsection, "icon_group", 0);
		TEX_INFO info;
		shared_str file = "ui\\ui_icon_equipment";
		if (place_id != 0) file.sprintf("ui\\ui_icon_equipment_%u", place_id);
		info.file = file;
			info.rect.x1 = pSettings->r_float(subsection, "inv_grid_x", 0) * 50;
			info.rect.x2 = (pSettings->r_float(subsection, "inv_grid_width", 0) * 50) + info.rect.x1;
			info.rect.y1 = pSettings->r_float(subsection, "inv_grid_y", 0) * 50;
			info.rect.y2 = (pSettings->r_float(subsection, "inv_grid_height", 0) * 50) + info.rect.y1;
			//shared_str id = subsection;
		if (!CheckTextureExist(subsection.c_str())) m_textures.insert(mk_pair(subsection,info));
	}
}

bool CUITextureMaster::IsSh(const char* texture_name){
	return strstr(texture_name,"\\") ? false : true;
}

void CUITextureMaster::InitTexture(const char* texture_name, IUISimpleTextureControl* tc){
	const char* ntn = CheckName(texture_name);
#ifdef DEBUG
	CTimer T;
	T.Start();
#endif

	xr_map<shared_str, TEX_INFO>::iterator	it;

	it = m_textures.find(ntn);

	if (it != m_textures.end())
	{
		tc->CreateShader(*((*it).second.file));
		tc->SetOriginalRectEx((*it).second.rect);
#ifdef DEBUG
		m_time += T.GetElapsed_ms();
#endif
		return;
	}
	tc->CreateShader(ntn);
#ifdef DEBUG
	m_time += T.GetElapsed_ms();
#endif
}

void CUITextureMaster::InitTexture(const char* texture_name, const char* shader_name, IUISimpleTextureControl* tc){
	const char* ntn = CheckName(texture_name);
#ifdef DEBUG
	CTimer T;
	T.Start();
#endif

	xr_map<shared_str, TEX_INFO>::iterator	it;

	it = m_textures.find(ntn);

	if (it != m_textures.end())
	{
		tc->CreateShader(*((*it).second.file), shader_name);
		tc->SetOriginalRectEx((*it).second.rect);
#ifdef DEBUG
		m_time += T.GetElapsed_ms();
#endif
		return;
	}
	tc->CreateShader(ntn, shader_name);
#ifdef DEBUG
	m_time += T.GetElapsed_ms();
#endif
}

float CUITextureMaster::GetTextureHeight(const char* texture_name){
	const char* ntn = CheckName(texture_name);
	xr_map<shared_str, TEX_INFO>::iterator	it;
	it = m_textures.find(ntn);

	if (it != m_textures.end())
		return (*it).second.rect.height();
	// KD: we don't need to die :)
//	R_ASSERT3(false,"CUITextureMaster::GetTextureHeight Can't find texture", texture_name);
	//ParseConfigIcon(texture_name);
	Msg("! CUITextureMaster::GetTextureHeight Can't find texture", texture_name);
	return 0;
}

bool CUITextureMaster::CheckTextureExist(const char* texture_name){
	//const char* ntn = CheckName(texture_name);
	xr_map<shared_str, TEX_INFO>::iterator	it;
	it = m_textures.find(texture_name);

	if (it != m_textures.end())
		return true;
	// Zander: I want to know if such a texture exists in advance
	return false;
}

Frect CUITextureMaster::GetTextureRect(const char* texture_name){
	const char* ntn = CheckName(texture_name);
	xr_map<shared_str, TEX_INFO>::iterator	it;
	it = m_textures.find(ntn);
	if (it != m_textures.end())
		return (*it).second.rect;

	// KD: we don't need to die :)
//	R_ASSERT3(false,"CUITextureMaster::GetTextureHeight Can't find texture", texture_name);
	//ParseConfigIcon(texture_name);
	Msg("! CUITextureMaster::GetTextureRect Can't find texture", texture_name);
	return Frect();
}

float CUITextureMaster::GetTextureWidth(const char* texture_name){
	const char* ntn = CheckName(texture_name);
	xr_map<shared_str, TEX_INFO>::iterator	it;
	it = m_textures.find(ntn);

	if (it != m_textures.end())
		return (*it).second.rect.width();
	// KD: we don't need to die :)
//	R_ASSERT3(false,"CUITextureMaster::GetTextureHeight Can't find texture", texture_name);
	//ParseConfigIcon(texture_name);
	Msg("! CUITextureMaster::GetTextureWidth Can't find texture", texture_name);
	return 0;
}

LPCSTR CUITextureMaster::GetTextureFileName(const char* texture_name){
	const char* ntn = CheckName(texture_name);
	xr_map<shared_str, TEX_INFO>::iterator	it;
	it = m_textures.find(ntn);

	if (it != m_textures.end())
		return *((*it).second.file);
	// KD: we don't need to die :)
//	R_ASSERT3(false,"CUITextureMaster::GetTextureHeight Can't find texture", texture_name);
	Msg("! CUITextureMaster::GetTextureFileName Can't find texture", texture_name);
	//ParseConfigIcon(texture_name);
	return 0;
}

TEX_INFO CUITextureMaster::FindItem(LPCSTR texture_name, LPCSTR def_texture_name)
{
	const char* ntn = CheckName(texture_name);
	xr_map<shared_str, TEX_INFO>::iterator	it;
	it = m_textures.find(ntn);

	if (it != m_textures.end())
		return (it->second);
	else{
		R_ASSERT2(m_textures.find(def_texture_name)!=m_textures.end(),ntn);
		return FindItem	(def_texture_name,NULL);
	}
}

void CUITextureMaster::GetTextureShader(LPCSTR texture_name, ui_shader& sh){
	const char* ntn = CheckName(texture_name);
	xr_map<shared_str, TEX_INFO>::iterator	it;
	it = m_textures.find(ntn);

//	R_ASSERT3(it != m_textures.end(), "can't find texture", texture_name);
	if (it == m_textures.end())
		Msg("! CUITextureMaster::GetTextureShader Can't find texture", texture_name);

	sh->create("hud\\default", *((*it).second.file));	
}

 

 


 

 

 


 

Скрытый текст


 

Описание: Функции чтения данных из конфига, принимают значения по умолчанию, возвращаемые в случае если нет такой строки/секции.

+ Эта правка необходима для предыдущей.

xrCore/xr_ini.h


#pragma once

class   CInifile;
struct  xr_token;

class XRCORE_API CInifile {
 public:
  using Item = std::pair<shared_str, shared_str>;
  struct XRCORE_API Sect {
    shared_str Name;
    string_unordered_map<shared_str, shared_str> Data;
    std::vector<Item> Unordered;
    BOOL line_exist ( LPCSTR, LPCSTR* = nullptr );
    u32 line_count();

	LPCSTR		r_string( LPCSTR );
	u32			r_u32( LPCSTR );
	float		r_float( LPCSTR );

	Ivector2	r_ivector2( LPCSTR );
	Fvector3	r_fvector3( LPCSTR );
  };
  using Root = string_unordered_map<shared_str, Sect*>;

  static_assert(std::is_same_v<Root::key_equal, string_hash::transparent_key_equal>, "Invalid key_equal"); //Нужная проверка в будущем, не убирать!

  // factorisation 
  static CInifile* Create  ( LPCSTR, BOOL = TRUE );
  static void      Destroy ( CInifile* );
  static IC BOOL   IsBOOL  ( LPCSTR B ) {
    return ( xr_strcmp( B, "on" ) == 0
             || xr_strcmp( B, "yes" ) == 0
             || xr_strcmp( B, "true" ) == 0
             || xr_strcmp( B, "1" ) == 0 );
  }

 private:
  LPSTR fName;
  Root  DATA;
  void  Load ( IReader*, LPCSTR );

 public:
  bool bReadOnly;
  BOOL bSaveAtEnd;

 public:
  CInifile ( IReader*, LPCSTR = 0 );
  CInifile ( LPCSTR, BOOL = TRUE, BOOL = TRUE, BOOL = TRUE );
  virtual ~CInifile ();

  bool save_as ( LPCSTR = 0 );
  std::string get_as_string();

  LPCSTR fname () { return fName; };

  Sect& r_section      ( LPCSTR );
  Sect& r_section      ( const shared_str& );
  BOOL  line_exist     ( LPCSTR, LPCSTR );
  BOOL  line_exist     ( const shared_str&, const shared_str& );
  u32   line_count     ( LPCSTR );
  u32   line_count     ( const shared_str& );
  BOOL  section_exist  ( LPCSTR );
  BOOL  section_exist  ( const shared_str& );
  Root& sections       () { return DATA; }
  const Root& sections () const { return DATA; }

  CLASS_ID r_clsid    ( LPCSTR, LPCSTR );
  CLASS_ID r_clsid    ( const shared_str& S, LPCSTR L ) {
    return r_clsid( S.c_str(), L );
  }

  LPCSTR   r_string   ( LPCSTR, LPCSTR ); // оставляет кавычки
  LPCSTR   r_string   ( const shared_str& S, LPCSTR L ) {
    return r_string( S.c_str(), L );
  } // оставляет кавычки

  shared_str r_string_wb ( LPCSTR, LPCSTR ); // убирает кавычки
  shared_str r_string_wb ( const shared_str& S, LPCSTR L ) {
    return r_string_wb( S.c_str(), L );
  } // убирает кавычки
  
  shared_str r_string( LPCSTR, LPCSTR, shared_str );
  
  LPCSTR r_string( LPCSTR, LPCSTR, LPCSTR );
  LPCSTR r_string ( const shared_str& S, LPCSTR L, LPCSTR Defstr ) {
    return r_string( S.c_str(), L, Defstr );
  }
  LPCSTR r_string ( const shared_str& S, LPCSTR L, shared_str Defstr ) {
    return r_string( S.c_str(), L, Defstr.c_str() );
  }

  u8       r_u8       ( LPCSTR, LPCSTR );
  u8       r_u8       ( const shared_str& S, LPCSTR L ) {
    return r_u8( S.c_str(), L );
  }

  u16      r_u16      ( LPCSTR, LPCSTR );
  u16      r_u16      ( const shared_str& S, LPCSTR L ) {
    return r_u16( S.c_str(), L );
  }

  u32      r_u32      ( LPCSTR, LPCSTR );
  u32      r_u32      ( const shared_str& S, LPCSTR L ) {
    return r_u32( S.c_str(), L );
  }

  s8       r_s8       ( LPCSTR, LPCSTR );
  s8       r_s8       ( const shared_str& S, LPCSTR L ) {
    return r_s8( S.c_str(), L );
  }

  s16      r_s16      ( LPCSTR, LPCSTR );
  s16      r_s16      ( const shared_str& S, LPCSTR L ) {
    return r_s16( S.c_str(), L );
  }

  s32      r_s32      ( LPCSTR, LPCSTR );
  s32      r_s32      ( const shared_str& S, LPCSTR L ) {
    return r_s32( S.c_str(), L );
  }

  float    r_float    ( LPCSTR, LPCSTR );
  float    r_float    ( const shared_str& S, LPCSTR L ) {
    return r_float( S.c_str(), L );
  }

  float    r_float    ( LPCSTR, LPCSTR, float );
  float    r_float    ( const shared_str& S, LPCSTR L, float Defval ) {
    return r_float( S.c_str(), L, Defval );
  }

  Fcolor   r_fcolor   ( LPCSTR, LPCSTR );
  Fcolor   r_fcolor   ( const shared_str& S, LPCSTR L ) {
    return r_fcolor( S.c_str(), L );
  }

  u32      r_color    ( LPCSTR, LPCSTR );
  u32      r_color    ( const shared_str& S, LPCSTR L ) {
    return r_color( S.c_str(), L );
  }

  Ivector2 r_ivector2 ( LPCSTR, LPCSTR );
  Ivector2 r_ivector2 ( const shared_str& S, LPCSTR L ) {
    return r_ivector2( S.c_str(), L );
  }

  Ivector3 r_ivector3 ( LPCSTR, LPCSTR );
  Ivector3 r_ivector3 ( const shared_str& S, LPCSTR L ) {
    return r_ivector3( S.c_str(), L );
  }

  Ivector4 r_ivector4 ( LPCSTR, LPCSTR );
  Ivector4 r_ivector4 ( const shared_str& S, LPCSTR L ) {
    return r_ivector4( S.c_str(), L );
  }

  Fvector2 r_fvector2 ( LPCSTR, LPCSTR );
  Fvector2 r_fvector2 ( const shared_str& S, LPCSTR L ) {
    return r_fvector2( S.c_str(), L );
  }

  Fvector3 r_fvector3 ( LPCSTR, LPCSTR );
  Fvector3 r_fvector3 ( const shared_str& S, LPCSTR L ) {
    return r_fvector3( S.c_str(), L );
  }

  Fvector4 r_fvector4 ( LPCSTR, LPCSTR );
  Fvector4 r_fvector4 ( const shared_str& S, LPCSTR L ) {
    return r_fvector4( S.c_str(), L );
  }
  
  // Add by Zander
 

  Fvector2 r_fvec2 ( LPCSTR, LPCSTR, LPCSTR, u8 );
  Fvector2 r_fvec2 ( const shared_str& S, LPCSTR L, LPCSTR K, u8 I ) {
    return r_fvec2( S.c_str(), L, K, I );
  }

  Fvector3 r_fvec3 ( LPCSTR, LPCSTR, LPCSTR, LPCSTR, u8 );
  Fvector3 r_fvec3 ( const shared_str& S, LPCSTR L, LPCSTR K, LPCSTR M, u8 I ) {
    return r_fvec3( S.c_str(), L, K, M, I );
  }

  Fvector4 r_fvec4 ( LPCSTR, LPCSTR, LPCSTR, LPCSTR, LPCSTR, u8 );
  Fvector4 r_fvec4 ( const shared_str& S, LPCSTR L, LPCSTR K, LPCSTR M, LPCSTR N, u8 I ) {
    return r_fvec4( S.c_str(), L, K, M, N, I );
  }
  
  // end add

  BOOL     r_bool     ( LPCSTR, LPCSTR );
  BOOL     r_bool     ( const shared_str& S, LPCSTR L ) {
    return r_bool( S.c_str(), L );
  }

  int      r_token    ( LPCSTR, LPCSTR, const xr_token *token_list );
  BOOL     r_line     ( LPCSTR, int, LPCSTR*, LPCSTR* );
  BOOL     r_line     ( const shared_str&, int, LPCSTR*, LPCSTR* );

  void     w_string   ( LPCSTR, LPCSTR, LPCSTR );
  void     w_u8       ( LPCSTR, LPCSTR, u8     );
  void     w_u16      ( LPCSTR, LPCSTR, u16    );
  void     w_u32      ( LPCSTR, LPCSTR, u32    );
  void     w_s8       ( LPCSTR, LPCSTR, s8     );
  void     w_s16      ( LPCSTR, LPCSTR, s16    );
  void     w_s32      ( LPCSTR, LPCSTR, s32    );
  void     w_float    ( LPCSTR, LPCSTR, float  );
  void     w_fcolor   ( LPCSTR, LPCSTR, const Fcolor& );
  void     w_color    ( LPCSTR, LPCSTR, u32    );
  void     w_ivector2 ( LPCSTR, LPCSTR, const Ivector2& );
  void     w_ivector3 ( LPCSTR, LPCSTR, const Ivector3& );
  void     w_ivector4 ( LPCSTR, LPCSTR, const Ivector4& );
  void     w_fvector2 ( LPCSTR, LPCSTR, const Fvector2& );
  void     w_fvector3 ( LPCSTR, LPCSTR, const Fvector3& );
  void     w_fvector4 ( LPCSTR, LPCSTR, const Fvector4& );
  void     w_bool     ( LPCSTR, LPCSTR, bool   );

  void     remove_line ( LPCSTR, LPCSTR );
  void     remove_section( LPCSTR );
};

// Main configuration file
extern XRCORE_API CInifile *pSettings;

 

xrCore/xr_ini.cpp


#include "stdafx.h"


#include "fs_internal.h"

XRCORE_API CInifile *pSettings = nullptr;


CInifile* CInifile::Create( const char* szFileName, BOOL ReadOnly ) {
  return xr_new<CInifile>( szFileName, ReadOnly );
}

void CInifile::Destroy( CInifile* ini ) {
  xr_delete( ini );
}


//Тело функций Inifile
XRCORE_API void _parse( LPSTR dest, LPCSTR src ) {
  if ( src ) {
    bool bInsideSTR = false;
    while ( *src ) {
      if ( isspace( (u8)*src ) ) {
        if ( bInsideSTR ) { *dest++ = *src++; continue; }
        while ( *src && iswspace( *src ) ) src++;
        continue;
      }
      else if ( *src == '"' ) {
        bInsideSTR = !bInsideSTR;
      }
      *dest++ = *src++;
    }
  }
  *dest = 0;
}


XRCORE_API void _decorate( LPSTR dest, LPCSTR src ) {
  if (src) {
    bool bInsideSTR = false;
    while ( *src ) {
      if ( *src == ',' ) {
        if ( bInsideSTR )
          *dest++ = *src++;
        else {
          *dest++ = *src++;
          *dest++ = ' ';
        }
        continue;
      }
      else if ( *src=='"' ) {
        bInsideSTR = !bInsideSTR;
      }
      *dest++ = *src++;
    }
  }
  *dest = 0;
}


BOOL CInifile::Sect::line_exist( LPCSTR L, LPCSTR* val ) {
  shared_str s( L );
  const auto A = Data.find( s );
  if (A != Data.end() ){
    if ( val ) *val = *A->second;
    return TRUE;
  }
  return FALSE;
}

LPCSTR CInifile::Sect::r_string( LPCSTR L ) {
	if (!L || !strlen(L)) //--#SM+#-- [fix for one of "xrDebug - Invalid handler" error log]
		Msg("!![ERROR] CInifile::Sect::r_string: S = [%s], L = [%s]", Name.c_str(), L);

	shared_str k(L);
	const auto A = Data.find(k);
	if (A != Data.end())
		return A->second.c_str();
	else
		FATAL("Can't find variable %s in [%s]", L, Name.c_str());
	return 0;
}

float CInifile::Sect::r_float( LPCSTR L ) {
	LPCSTR C = r_string(L);
	return float(atof(C));
}

u32 CInifile::Sect::r_u32( LPCSTR L ) {
	LPCSTR C = r_string(L);
	return u32(atoi(C));
}

Fvector3 CInifile::Sect::r_fvector3( LPCSTR L ) {
	LPCSTR   C = r_string(L);
	Fvector3 V = { 0.f, 0.f, 0.f };
	sscanf(C, "%f,%f,%f", &V.x, &V.y, &V.z);
	return V;
}

Ivector2 CInifile::Sect::r_ivector2( LPCSTR L ) {
	LPCSTR   C = r_string(L);
	Ivector2 V = { 0, 0 };
	sscanf(C, "%d,%d", &V.x, &V.y);
	return V;
}

u32 CInifile::Sect::line_count() {
	return u32( Data.size() );
}

CInifile::CInifile( IReader* F, LPCSTR path ) {
  fName      = 0;
  bReadOnly  = true;
  bSaveAtEnd = FALSE;
  Load( F, path );
}


CInifile::CInifile( LPCSTR szFileName, BOOL ReadOnly, BOOL bLoad, BOOL SaveAtEnd ) {
  fName       = szFileName ? xr_strdup( szFileName ) : 0;
  bReadOnly   = !!ReadOnly;
  bSaveAtEnd  = SaveAtEnd;
  if ( bLoad ) {
    string_path path, folder;
    _splitpath( fName, path, folder, 0, 0 );
    strcat( path, folder );
    IReader* R = FS.r_open( szFileName );
    if ( R ) {
      Load( R, path );
      FS.r_close( R );
    }
  }
}


CInifile::~CInifile() {
  if ( !bReadOnly && bSaveAtEnd ) {
    if ( !save_as() )
      Log( "!Can't save inifile:", fName );
  }
  xr_free( fName );
  for ( auto &I : DATA ) xr_delete( I.second );
}


static void insert_item( CInifile::Sect *tgt, const CInifile::Item& I ) {
  auto sect_it = tgt->Data.find( I.first );
  if ( sect_it != tgt->Data.end() && sect_it->first.equal( I.first ) ) {
    sect_it->second = I.second;
    auto found = std::find_if(
      tgt->Unordered.begin(), tgt->Unordered.end(),
      [&]( const auto &it ) {
        return xr_strcmp( *it.first, *I.first ) == 0;
      }
    );
    if ( found != tgt->Unordered.end() ) {
      found->second = I.second;
    }
  }
  else {
    tgt->Data.insert({ I.first, I.second });
    tgt->Unordered.push_back( I );
  }
}


void CInifile::Load ( IReader* F, LPCSTR path ) {
  R_ASSERT( F );
  Sect       *Current = nullptr;
  string4096 str;
  string4096 str2;

  while ( !F->eof() ) {
    F->r_string( str, sizeof( str ) );
    _Trim( str );
    LPSTR semi   = strchr( str, ';' );
    LPSTR semi_1 = strchr( str, '/' );

    if ( semi_1 && ( *(semi_1 + 1) == '/' ) && ( (!semi) || ( semi && ( semi_1 < semi ) ) ) )
      semi = semi_1;

    if ( semi )
      *semi = 0;

    if ( str[ 0 ] && ( str[ 0 ] == '#' ) && strstr( str, "#include" ) ) {
      string64 inc_name;
      R_ASSERT( path && path[ 0 ] );
      if ( _GetItem( str, 1, inc_name, '"' ) ) {
        string_path fn, inc_path, folder;
        strconcat( sizeof( fn ), fn, path, inc_name );
        _splitpath( fn, inc_path, folder, 0, 0 );
        strcat( inc_path, folder );
        IReader* I = FS.r_open( fn );
        R_ASSERT3( I, "Can't find include file:", inc_name );
        Load( I, inc_path );
        FS.r_close( I );
      }
    }
    else if ( str[ 0 ] && ( str[ 0 ] == '[' ) ) {
      // insert previous filled section
      if ( Current ) {
        auto I = DATA.find( Current->Name );
        if ( I != DATA.end() )
          FATAL( "Duplicate section '%s' found.", Current->Name.c_str() );
        DATA.insert({ Current->Name, Current });
      }
      Current = xr_new<Sect>();
      Current->Name = 0;
      // start new section
      R_ASSERT3( strchr( str, ']' ), "Bad ini section found: ", str );
      LPCSTR inherited_names = strstr( str, "]:" );
      if ( 0 != inherited_names ) {
        VERIFY2( bReadOnly, "Allow for readonly mode only." );
        inherited_names += 2;
        int cnt = _GetItemCount( inherited_names );
        for ( int k = 0; k < cnt; ++k ) {
          xr_string tmp;
          _GetItem( inherited_names, k, tmp );
          Sect& inherited = r_section( tmp.c_str() );
          for ( auto &it : inherited.Data ) {
            Item I = { it.first, it.second };
            insert_item( Current, I );
          }
        }
      }
      *strchr( str, ']' ) = 0;
      Current->Name = strlwr( str + 1 );
    }
    else {
      if ( Current ) {
        char* name = str;
        char* t    = strchr( name, '=' );
        if ( t ) {
          *t = 0;
          _Trim( name );
          _parse( str2, ++t );
        }
        else {
          _Trim( name );
          str2[ 0 ] = 0;
        }

        if ( name[ 0 ] ) {
          Item I;
          I.first  = name;
          I.second = str2[ 0 ] ? str2 : 0;
          if ( bReadOnly ) {
            if ( *I.first )
              insert_item( Current, I );
          }
          else {
            if ( *I.first || *I.second )
              insert_item( Current, I );
          }
        }
      }
    }
  }

  if ( Current ) {
    auto I = DATA.find( Current->Name );
    if ( I != DATA.end() )
      FATAL( "Duplicate section '%s' found.", Current->Name.c_str() );
    DATA.insert({ Current->Name, Current });
  }
}


bool CInifile::save_as( LPCSTR new_fname ) {
  // save if needed
  if ( new_fname && new_fname[ 0 ] ) {
    xr_free( fName );
    fName = xr_strdup( new_fname );
  }
  R_ASSERT( fName && fName[ 0 ] );
  IWriter* F = FS.w_open_ex( fName );
  if ( F ) {
    string512 temp, val;
    for ( const auto &r_it : DATA ) {
      sprintf_s( temp, sizeof( temp ), "[%s]", r_it.first.c_str() );
      F->w_string( temp );
      for ( const auto &I : r_it.second->Unordered ) {
        if ( *I.first ) {
          if ( *I.second ) {
            _decorate( val, *I.second );
            {
              // only name and value
              sprintf_s( temp, sizeof( temp ), "%8s%-32s = %-32s", " ", *I.first, val );
            }
          }
          else {
            {
              // only name
              sprintf_s( temp, sizeof( temp ), "%8s%-32s = ", " ", *I.first );
            }
          }
        }
        else {
          // no name, so no value
          temp[ 0 ] = 0;
        }
        _TrimRight( temp );
        if ( temp[ 0 ] ) F->w_string( temp );
      }
      F->w_string( " " );
    }
    FS.w_close( F );
    return true;
  }
  return false;
}


BOOL CInifile::section_exist( LPCSTR S ) {
  shared_str k( S );
  const auto I = DATA.find( k );
  return I != DATA.end();
}


BOOL CInifile::line_exist( LPCSTR S, LPCSTR L ) {
  if ( !section_exist( S ) ) return FALSE;
  Sect&   I = r_section( S );
  shared_str k( L );
  const auto A = I.Data.find( k );
  return A != I.Data.end();
}

u32 CInifile::line_count( LPCSTR Sname ) {
  Sect& S = r_section( Sname );
  return S.line_count();
}


CInifile::Sect& CInifile::r_section ( const shared_str& S ) {
  return r_section( S.c_str() );
}


BOOL CInifile::line_exist ( const shared_str& S, const shared_str& L ) {
  return line_exist( S.c_str(), L.c_str() );
}


u32 CInifile::line_count ( const shared_str& S ) {
  return line_count( S.c_str() );
}


BOOL CInifile::section_exist ( const shared_str& S ) {
  return section_exist( S.c_str() );
}


CInifile::Sect& CInifile::r_section( LPCSTR S ) {
  R_ASSERT( S && strlen( S ), "Empty section (null\\'') passed into CInifile::r_section(). See info above ^, check your configs and 'call stack'." ); //--#SM+#--

  char section[ 256 ];
  strcpy_s( section, sizeof( section ), S );
  shared_str k = strlwr( section );
  const auto I = DATA.find( k );
  if ( I == DATA.end() )
    FATAL( "Can't open section '%s'", S );
  return  *I->second;
}

LPCSTR CInifile::r_string ( LPCSTR S, LPCSTR L ) {
  if ( !S || !L || !strlen( S ) || !strlen( L ) ) //--#SM+#-- [fix for one of "xrDebug - Invalid handler" error log]
    Msg( "!![ERROR] CInifile::r_string: S = [%s], L = [%s]", S, L );

  Sect&   I = r_section( S );
  shared_str k( L );
  const auto A = I.Data.find( k );
  if ( A != I.Data.end() )
    return A->second.c_str();
  else
    FATAL( "Can't find variable %s in [%s]", L, S );
  return 0;
}

shared_str CInifile::r_string ( LPCSTR S, LPCSTR L, shared_str Defstr ) {
  if ( !S || !L || !strlen( S ) || !strlen( L ) ) //--#SM+#-- [fix for one of "xrDebug - Invalid handler" error log]
    Msg( "!![ERROR] CInifile::r_string: S = [%s], L = [%s]", S, L );

  Sect&   I = r_section( S );
  shared_str k( L );
  const auto A = I.Data.find( k );
  if ( A != I.Data.end() )
    return A->second.c_str();
  else
    return Defstr;
}

LPCSTR CInifile::r_string ( LPCSTR S, LPCSTR L, LPCSTR Defstr ) {
  if ( !S || !L || !strlen( S ) || !strlen( L ) ) //--#SM+#-- [fix for one of "xrDebug - Invalid handler" error log]
    Msg( "!![ERROR] CInifile::r_string: S = [%s], L = [%s]", S, L );

  Sect&   I = r_section( S );
  shared_str k( L );
  const auto A = I.Data.find( k );
  if ( A != I.Data.end() )
    return A->second.c_str();
  else
    return Defstr;
}


shared_str CInifile::r_string_wb( LPCSTR S, LPCSTR L ) {
  LPCSTR _base = r_string( S, L );
  if ( 0 == _base ) return  shared_str( 0 );
  string512 _original;
  strcpy_s( _original, _base );
  u32 _len = xr_strlen( _original );
  if ( 0 == _len ) return  shared_str( "" );
  if ( '"' == _original[ _len - 1 ] ) _original[ _len - 1 ] = 0; // skip end
  if ( '"' == _original[ 0 ] ) return  shared_str( &_original[ 0 ] + 1 ); // skip begin
  return shared_str( _original );
}


u8 CInifile::r_u8 ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  return u8( atoi( C ) );
}


u16 CInifile::r_u16 ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  return u16( atoi( C ) );
}


u32 CInifile::r_u32 ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  return u32( atoi( C ) );
}


s8 CInifile::r_s8 ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  return s8( atoi( C ) );
}


s16 CInifile::r_s16 ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  return s16( atoi( C ) );
}


s32 CInifile::r_s32 ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  return s32( atoi( C ) );
}


float CInifile::r_float ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  return float( atof( C ) );
}

float CInifile::r_float ( LPCSTR S, LPCSTR L, float Defval ) {
	if(!line_exist( S, L )) return Defval;
  LPCSTR C = r_string( S, L);
  return float( atof( C ) );
}


Fcolor CInifile::r_fcolor ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  Fcolor V = { 0, 0, 0, 0 };
  sscanf( C, "%f,%f,%f,%f", &V.r, &V.g, &V.b, &V.a );
  return V;
}


u32 CInifile::r_color ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  u32    r = 0, g = 0, b = 0, a = 255;
  sscanf( C, "%d,%d,%d,%d", &r, &g, &b, &a );
  return color_rgba( r, g, b, a );
}


Ivector2 CInifile::r_ivector2 ( LPCSTR S, LPCSTR L ) {
  LPCSTR   C = r_string( S, L );
  Ivector2 V = { 0, 0 };
  sscanf( C, "%d,%d", &V.x, &V.y );
  return V;
}


Ivector3 CInifile::r_ivector3 ( LPCSTR S, LPCSTR L ) {
  LPCSTR  C = r_string( S, L );
  Ivector V = { 0, 0, 0 };
  sscanf( C, "%d,%d,%d", &V.x, &V.y, &V.z );
  return V;
}


Ivector4 CInifile::r_ivector4 ( LPCSTR S, LPCSTR L ) {
  LPCSTR   C = r_string( S, L );
  Ivector4 V = { 0, 0, 0, 0 };
  sscanf( C, "%d,%d,%d,%d", &V.x, &V.y, &V.z, &V.w );
  return V;
}


Fvector2 CInifile::r_fvector2 ( LPCSTR S, LPCSTR L ) {
  LPCSTR   C = r_string( S, L );
  Fvector2 V = { 0.f, 0.f };
  sscanf( C, "%f,%f", &V.x, &V.y );
  return V;
}


Fvector3 CInifile::r_fvector3 ( LPCSTR S, LPCSTR L ) {
  LPCSTR   C = r_string( S, L );
  Fvector3 V = { 0.f, 0.f, 0.f };
  sscanf( C, "%f,%f,%f", &V.x, &V.y, &V.z );
  return V;
}


Fvector4 CInifile::r_fvector4 ( LPCSTR S, LPCSTR L ) {
  LPCSTR   C = r_string( S, L );
  Fvector4 V = { 0.f, 0.f, 0.f, 0.f };
  sscanf( C, "%f,%f,%f,%f", &V.x, &V.y, &V.z, &V.w );
  return V;
}

// Add by Zander

Fvector2 CInifile::r_fvec2 ( LPCSTR S, LPCSTR L, LPCSTR K, u8 I ) {
	Msg("CInifile::r_fvec2 [%s], [%s], [%s], [%f]", S, L, K, I);
	LPCSTR   C1 = r_string( S, L, "0.0" );
	LPCSTR   C2 = r_string( S, K, "0.0" );
	string128 a;
	float f = 0.f;
	shared_str tl;
	Fvector2 V = { 0.f, 0.f };
	if (_GetItemCount( C1 ) > I) {
		f = float(atof(_GetItem(C1, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C1, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.x);
	}
	else { f = 0.f; V.x = 0.f; }
	Msg("CInifile::r_fvec2 549 [%f], [%s]", f, tl.c_str());
	if (_GetItemCount( C2 ) > I) {
		f = float(atof(_GetItem(C2, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C2, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.y);
	}
	else { f = 0.f; V.y = 0.f; }
	Msg("CInifile::r_fvec2 556 [%f], [%s]", f, tl.c_str());
	Msg("Return: V.x[%f], V.y[%f]", V.x, V.y);
	return V;
}


Fvector3 CInifile::r_fvec3 ( LPCSTR S, LPCSTR L, LPCSTR K, LPCSTR M, u8 I ) {
	Msg("CInifile::r_fvec3 [%s], [%s], [%s], [%s], [%f]", S, L, K, M, I);
	LPCSTR   C1 = r_string( S, L, "0.0" );
	LPCSTR   C2 = r_string( S, K, "0.0" );
	LPCSTR   C3 = r_string( S, M, "0.0" );
	string128 a;
	float f = 0.f;
	shared_str tl;
	Fvector3 V = { 0.f, 0.f, 0.f };
	if (_GetItemCount( C1 ) > I) {
		f = float(atof(_GetItem(C1, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C1, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.x);
	}
	else { f = 0.f; V.x = 0.f; }
	Msg("CInifile::r_fvec3 577 [%f], [%s]", f, tl.c_str());
	if (_GetItemCount( C2 ) > I) {
		f = float(atof(_GetItem(C2, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C2, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.y);
	}
	else { f = 0.f; V.y = 0.f; }
	Msg("CInifile::r_fvec3 584 [%f], [%s]", f, tl.c_str());
	if (_GetItemCount( C3 ) > I) {
		f = float(atof(_GetItem(C3, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C3, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.z);
	}
	else { f = 0.f; V.z = 0.f; }
	Msg("CInifile::r_fvec3 591 [%f], [%s]", f, tl.c_str());
	Msg("Return: V.x[%f], V.y[%f], V.z[%f]", V.x, V.y, V.z);
	return V;
}


Fvector4 CInifile::r_fvec4 ( LPCSTR S, LPCSTR L, LPCSTR K, LPCSTR M, LPCSTR N, u8 I ) {
	Msg("CInifile::r_fvec4 [%s], [%s], [%s], [%s], [%s], [%f]", S, L, K, M, N, I);
	LPCSTR   C1 = r_string( S, L, "0.0" );
	LPCSTR   C2 = r_string( S, K, "0.0" );
	LPCSTR   C3 = r_string( S, M, "0.0" );
	LPCSTR   C4 = r_string( S, N, "0.0" );
	string128 a;
	float f = 0.f;
	shared_str tl;
	Fvector4 V = { 0.f, 0.f, 0.f, 0.f };
	if (_GetItemCount( C1 ) > I) {
		f = float(atof(_GetItem(C1, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C1, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.x);
	}
	else { f = 0.f; V.x = 0.f; }
	Msg("CInifile::r_fvec4 613 [%f], [%s]", f, tl.c_str());
	if (_GetItemCount( C2 ) > I) {
		f = float(atof(_GetItem(C2, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C2, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.y);
	}
	else { f = 0.f; V.y = 0.f; }
	Msg("CInifile::r_fvec4 620 [%f], [%s]", f, tl.c_str());
	if (_GetItemCount( C3 ) > I) {
		f = float(atof(_GetItem(C3, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C3, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.z);
	}
	else { f = 0.f; V.z = 0.f; }
	Msg("CInifile::r_fvec4 627 [%f], [%s]", f, tl.c_str());
	if (_GetItemCount( C4 ) > I) {
		f = float(atof(_GetItem(C4, I, a, ',', "0.0", true)));
		tl = shared_str(_GetItem(C4, I, a, ',', "0.0", true));
		sscanf(tl.c_str(), "%f", &V.w);
	}
	else { f = 0.f; V.w = 0.f; }
	Msg("CInifile::r_fvec4 634 [%f], [%s]", f, tl.c_str());
	Msg("Return: V.x[%f], V.y[%f], V.z[%f], V.w[%f]", V.x, V.y, V.z, V.w);
	return V;
}

// end add

BOOL CInifile::r_bool ( LPCSTR S, LPCSTR L ) {
  LPCSTR C = r_string( S, L );
  char   B[ 8 ];
  strncpy( B, C, 7 );
  strlwr( B );
  return IsBOOL( B );
}


CLASS_ID CInifile::r_clsid ( LPCSTR S, LPCSTR L) {
  LPCSTR C = r_string( S, L );
  return TEXT2CLSID( C );
}


int CInifile::r_token ( LPCSTR S, LPCSTR L, const xr_token *token_list ) {
  LPCSTR C = r_string( S, L );
  for( int i = 0; token_list[ i ].name; i++ )
    if( !stricmp( C, token_list[ i ].name ) )
      return token_list[ i ].id;
  return 0;
}


BOOL CInifile::r_line( LPCSTR S, int L, const char** N, const char** V ) {
  Sect& SS = r_section( S );
  if ( L >= (int)SS.Unordered.size() || L < 0 ) return FALSE;
  const auto &I = SS.Unordered.at( L );
  *N = I.first.c_str();
  *V = I.second.c_str();
  return TRUE;
}


BOOL CInifile::r_line( const shared_str& S, int L, const char** N, const char** V ) {
  return r_line( S.c_str(), L, N, V );
}


void CInifile::w_string ( LPCSTR S, LPCSTR L, LPCSTR V ) {
  R_ASSERT( !bReadOnly );

  // section
  char sect[ 256 ];
  _parse( sect, S );
  _strlwr( sect );
  ASSERT_FMT( sect[ 0 ], "[%s]: wrong section name [%s]", __FUNCTION__, S );
  if ( !section_exist( sect ) ) {
    // create _new_ section
    Sect *NEW = xr_new<Sect>();
    NEW->Name = sect;
    DATA.insert({ NEW->Name, NEW });
  }

  // parse line/value
  char line [ 256 ];
  _parse( line, L );
  ASSERT_FMT( line[ 0 ], "[%s]: wrong param name [%s]", __FUNCTION__, L );
  char value[ 256 ];
  _parse( value, V );

  // duplicate & insert
  Item I;
  I.first  = line;
  I.second = value[ 0 ] ? value : 0;
  Sect* data = &r_section( sect );
  insert_item( data, I );
}


void CInifile::w_u8 ( LPCSTR S, LPCSTR L, u8 V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d", V );
  w_string( S, L, temp );
}


void CInifile::w_u16 ( LPCSTR S, LPCSTR L, u16 V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d", V );
  w_string( S, L, temp );
}


void CInifile::w_u32( LPCSTR S, LPCSTR L, u32 V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d", V );
  w_string( S, L, temp );
}


void CInifile::w_s8 ( LPCSTR S, LPCSTR L, s8 V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d", V );
  w_string( S, L, temp );
}


void CInifile::w_s16 ( LPCSTR S, LPCSTR L, s16 V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d", V );
  w_string( S, L, temp );
}


void CInifile::w_s32 ( LPCSTR S, LPCSTR L, s32 V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d", V );
  w_string( S, L, temp);
}


void CInifile::w_float ( LPCSTR S, LPCSTR L, float V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%f", V );
  w_string( S, L, temp );
}


void CInifile::w_fcolor ( LPCSTR S, LPCSTR L, const Fcolor& V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%f,%f,%f,%f", V.r, V.g, V.b, V.a );
  w_string( S, L, temp );
}


void CInifile::w_color ( LPCSTR S, LPCSTR L, u32 V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d,%d,%d,%d", color_get_R( V ), color_get_G( V ), color_get_B( V ), color_get_A( V ) );
  w_string( S, L, temp );
}



void CInifile::w_ivector2 ( LPCSTR S, LPCSTR L, const Ivector2& V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d,%d", V.x, V.y );
  w_string( S, L, temp );
}


void CInifile::w_ivector3 ( LPCSTR S, LPCSTR L, const Ivector3& V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d,%d,%d", V.x, V.y, V.z );
  w_string( S, L, temp );
}


void CInifile::w_ivector4 ( LPCSTR S, LPCSTR L, const Ivector4& V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%d,%d,%d,%d", V.x, V.y, V.z, V.w );
  w_string( S, L, temp );
}


void CInifile::w_fvector2 ( LPCSTR S, LPCSTR L, const Fvector2& V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%f,%f", V.x, V.y );
  w_string( S, L, temp );
}


void CInifile::w_fvector3 ( LPCSTR S, LPCSTR L, const Fvector3& V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%f,%f,%f", V.x, V.y, V.z );
  w_string( S, L, temp );
}


void CInifile::w_fvector4 ( LPCSTR S, LPCSTR L, const Fvector4& V ) {
  string128 temp;
  sprintf_s( temp, sizeof( temp ), "%f,%f,%f,%f", V.x, V.y, V.z, V.w );
  w_string( S, L, temp );
}


void CInifile::w_bool ( LPCSTR S, LPCSTR L, bool V ) {
  w_string( S, L, V ? "true" : "false" );
}


void CInifile::remove_line( LPCSTR S, LPCSTR L ) {
  R_ASSERT( !bReadOnly );

  if ( line_exist( S, L ) ) {
    Sect& data = r_section( S );
    shared_str k( L );
    auto A = data.Data.find( k );
    R_ASSERT( A != data.Data.end() );
    data.Data.erase( A );
    auto found = std::find_if(
      data.Unordered.begin(), data.Unordered.end(),
      [&]( const auto& it ) {
        return xr_strcmp( *it.first, L ) == 0;
      }
    );
    R_ASSERT( found != data.Unordered.end() );
    data.Unordered.erase( found );
  }
}


void CInifile::remove_section( LPCSTR S ) {
  R_ASSERT( !bReadOnly );

  if ( section_exist( S ) ) {
    shared_str k( S );
    const auto I = DATA.find( k );
    R_ASSERT( I != DATA.end() );
    DATA.erase( I );
  }
}


#include <sstream>

std::string CInifile::get_as_string() {
  std::stringstream str;

  bool first_sect = true;
  for ( const auto &r_it : DATA ) {
    if ( !first_sect ) str << "\r\n";
    first_sect = false;
    str << "[" << r_it.first.c_str() << "]\r\n";
    for ( const auto &I : r_it.second->Unordered ) {
      if ( I.first.c_str() ) {
        if ( I.second.c_str() ) {
          string512 val;
          _decorate( val, I.second.c_str() );
          _TrimRight( val );
          // only name and value
          str << I.first.c_str() << " = " << val << "\r\n";
        }
        else {
          // only name
          str << I.first.c_str() << " =\r\n";
        }
      }
    }
  }

  return str.str();
}


 

 

  • Нравится 1
  • Полезно 2

Мод, где не бывает одинаковых путей - Судьба Зоны. (Лучшее, что у меня получилось на X-Ray) На базе модифицированного движка OGSR Engine.

Бывший мододел на X-Ray / Начинающий игродел на Unreal Engine. Программист.

AMD Ryzen 9 7950X (16 ядер, 5.7ГГц); RTX 3080; 128 ГБ DDR5; Arctic Liquid Freezer II-420; 3 ТБ SSD PCIe 4.0; 4ТБ HDD.

Поделиться этим сообщением


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

    Ни один зарегистрированный пользователь не просматривает эту страницу.

AMK-Team.ru

×
×
  • Создать...