Zander_driver 10 333 Опубликовано 29 Ноября 2019 Делаю первые шаги в C++ Так что сильно не пинайте. Но, правочки такой, почему-то никто не выкладывал. Выложу тогда я. Скрытый текст Добавление возможности автокоррекции размеров иконок и сеток, в окнах слотов и инвентаря. Править потребовалось один единственный файл: xrGame/ui/UIXmlInit.cpp Один метод в нем, изменил вот так: bool CUIXmlInit::InitDragDropListEx(CUIXml& xml_doc, const char* path, int index, CUIDragDropListEx* pWnd) { R_ASSERT3(xml_doc.NavigateToNode(path,index), "XML node not found", path); float x = xml_doc.ReadAttribFlt(path, index, "x"); float y = xml_doc.ReadAttribFlt(path, index, "y"); float width = xml_doc.ReadAttribFlt(path, index, "width"); float height = xml_doc.ReadAttribFlt(path, index, "height"); InitAlignment (xml_doc, path, index, x, y, pWnd); pWnd->Init (x,y, width,height); Ivector2 w_cell_sz, w_cells, w_cell_sp; // Add by Zander Fvector2 device_scale; device_scale.x = 1024.0f / _max((float)Device.dwWidth, 1.0f); device_scale.y = 768.0f / _max((float)Device.dwHeight, 1.0f); int autclc = xml_doc.ReadAttribInt(path, index, "autocalc", 0); int cellmin = xml_doc.ReadAttribInt(path, index, "cell_minimals", 0); // end w_cell_sz.x = xml_doc.ReadAttribInt(path, index, "cell_width"); w_cell_sz.y = xml_doc.ReadAttribInt(path, index, "cell_height"); // Add by Zander if (autclc == 1) { w_cell_sz.x = (int)round(w_cell_sz.x * device_scale.x); w_cell_sz.y = (int)round(w_cell_sz.y * device_scale.y); } if (autclc == 2) { w_cell_sz.x = (int)round(50.0f * device_scale.x); w_cell_sz.y = (int)round(50.0f * device_scale.y); } // end w_cells.y = xml_doc.ReadAttribInt(path, index, "rows_num"); w_cells.x = xml_doc.ReadAttribInt(path, index, "cols_num"); // Add by Zander if (cellmin == 1) { // Приоритет - необходимое число ячеек в указанном объеме int w_cell_sz_x = (int)trunc(width / _max((float)w_cells.x, 1.0f)); int w_cell_sz_y = (int)trunc(height / _max((float)w_cells.y, 1.0f)); if (w_cell_sz_x < w_cell_sz.x) w_cell_sz.x = w_cell_sz_x; if (w_cell_sz_y < w_cell_sz.y) w_cell_sz.y = w_cell_sz_y; } else { // Приоритет - правильный размер иконок и сетки if (autclc == 1) { // Заполняем сетку максимально w_cells.x = (int)trunc(width / _max((float)w_cell_sz.x, 1.0f)); w_cells.y = (int)trunc(height / _max((float)w_cell_sz.y, 1.0f)); } } // end w_cell_sp.x = xml_doc.ReadAttribInt(path, index, "cell_sp_x", 0); w_cell_sp.y = xml_doc.ReadAttribInt(path, index, "cell_sp_y", 0); pWnd->SetCellSize (w_cell_sz); pWnd->SetCellsSpacing (w_cell_sp); pWnd->SetStartCellsCapacity (w_cells); int tmp = xml_doc.ReadAttribInt(path, index, "unlimited", 0); pWnd->SetAutoGrow (tmp!=0); tmp = xml_doc.ReadAttribInt(path, index, "group_similar", 0); pWnd->SetGrouping (tmp!=0); tmp = xml_doc.ReadAttribInt(path, index, "custom_placement", 1); pWnd->SetCustomPlacement(tmp!=0); tmp = xml_doc.ReadAttribInt(path, index, "vertical_placement", 0); pWnd->SetVerticalPlacement(tmp != 0); tmp = xml_doc.ReadAttribInt(path, index, "show_grid", 1); pWnd->SetDrawGrid (tmp != 0); tmp = xml_doc.ReadAttribInt(path, index, "condition_progress_bar", 0); pWnd->SetConditionProgBarVisibility(tmp!=0); if (xr_strlen(path)) pWnd->SetWindowName (path, TRUE); return true; } В результате этой правки, движок начинает понимать в конфигах xml инвентаря, два новых атрибута в окнах с инвентарными сетками: autocalc (возможные значения - 0,1,2) - необходимость пересчитывать размер ячеек сетки и иконок. cell_minimals (возможные значения - 0,1) - необходимость во что бы то ни стало вместить указанное количество ячеек в заданные размеры окна. Если эти атрибуты в конфигах отсутствуют, или указано значение 0 - то поведение в точности такое же, как и раньше. Сетка/иконки выводятся в координатах 1024х768, в тех размерах и координатах, что указаны в xml. autocalc="1" - ячейки иконок примут размер в пикселах, указанный в xml, на мониторе конечного пользователя с учетом его разрешения. Т.е. если например у нас написано <dragdrop_bag cell_width="42" cell_height="42" autocalc="1"/> То 1 клетка сетки/иконок примет размер 42х42 пиксела на мониторе юзера. И не важно, какое у него разрешение, и насколько оно (не)похоже на то, что имели в виду составители xml конфига. Ненужные в данном вопросе атрибуты xml в этой строке опущены, но удалять их конечно не надо. autocalc="2" - для ленивых) Поведение то же, что и в предыдущем варианте, но подгонять сетку будет к размеру 50х50, игнорируя указания cell_width, cell_height. cell_minimals="1" - Это для слотов. Если указано 1, окно слота маленькое, и указанное число ячеек в нем не помещается, то размер ячейки дополнительно ужимается до того чтобы поместилось. Это все опять же на экране пользователя с учетом его разрешения. Ну и бонусом, если для окна инвентаря указать autocalc 1 или 2, и cell_minimals 0, то размер ячейки будет изменен с учетом разрешения монитора пользователя, а сетка инвентаря переформирована под то число ячеек, которое в нее поместится с учетом этого изменения. Игнорируя указанные в xml значения rows_num и cols_num. У себя проверил - работает... может быть, пригодится кому-то) 1 3 4 Мод, где не бывает одинаковых путей - Судьба Зоны. (Лучшее, что у меня получилось на X-Ray) На базе модифицированного движка OGSR Engine. Бывший мододел на X-Ray / Начинающий игродел на Unreal Engine. Программист. AMD Ryzen 9 7950X (16 ядер, 32 потока, 5.75 ГГц); RTX 3080; 128 ГБ DDR5; Arctic Liquid Freezer II-420; 3 ТБ SSD PCIe 4.0; 4ТБ HDD. Поделиться этим сообщением Ссылка на сообщение
Zander_driver 10 333 Опубликовано 12 Января 2020 Подкину еще немножко своих правок, для движка 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 ядер, 32 потока, 5.75 ГГц); RTX 3080; 128 ГБ DDR5; Arctic Liquid Freezer II-420; 3 ТБ SSD PCIe 4.0; 4ТБ HDD. Поделиться этим сообщением Ссылка на сообщение