mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-09-10 12:02:53 +02:00
Parse INI files with OFINIFile
This commit is contained in:
parent
fbedf4361d
commit
ae3cfe9344
10 changed files with 88 additions and 359 deletions
|
@ -107,9 +107,9 @@ mkxp-z provides limited support for some WinAPI functions that would normally br
|
|||
* `GetSystemMetrics`: Only supports getting screen width/height.
|
||||
* `SetCapture`: No-op. Always returns `571`.
|
||||
* `ReleaseCapture`: No-op.
|
||||
* `GetPrivateProfileString`: Emulated with MKXP's ini code.
|
||||
* `GetPrivateProfileString`: Emulated with OFINIFile.
|
||||
* `GetUserDefaultLangId`: Checks for JP, EN, FR, IT, DE, ES, KO, PT and ZH. Returns English (`0x09`) if the locale can't be determined. Doesn't handle sublanguages.
|
||||
* `GetUserName`: Returns the `$USER` environment variable, or `Ditto` if it doesn't exist.
|
||||
* `GetUserName`: *(macOS)* Gets user login name. *(Linux)* Returns the `$USER` environment variable, or `Ditto` if it doesn't exist.
|
||||
|
||||
## Nonstandard RGSS extensions
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ endif
|
|||
|
||||
# Objectify our C
|
||||
global_args += run_command(objfw,'--cppflags').stdout().split()
|
||||
add_project_arguments(run_command(objfw,'--objcflags').stdout().split(), language:'objc')
|
||||
add_project_arguments(run_command(objfw,'--objcflags').stdout().split(), language:['objc','objcpp'])
|
||||
add_project_link_arguments(run_command(objfw,'--libs','--ldflags').stdout().split(), language:['objc','objcpp'])
|
||||
|
||||
# Make sure to use ARC
|
||||
|
|
|
@ -19,114 +19,28 @@
|
|||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#import <ObjFW/ObjFW.h>
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#import "config.h"
|
||||
|
||||
#include <SDL_filesystem.h>
|
||||
#import <boost/program_options/options_description.hpp>
|
||||
#import <boost/program_options/parsers.hpp>
|
||||
#import <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <stdint.h>
|
||||
#import <SDL_filesystem.h>
|
||||
|
||||
#include "debugwriter.h"
|
||||
#include "util.h"
|
||||
#include "sdl-util.h"
|
||||
#include "iniconfig.h"
|
||||
#import <fstream>
|
||||
#import <stdint.h>
|
||||
|
||||
#import "debugwriter.h"
|
||||
#import "util.h"
|
||||
#import "sdl-util.h"
|
||||
|
||||
#ifdef HAVE_DISCORDSDK
|
||||
#include <discord_game_sdk.h>
|
||||
#include "discordstate.h"
|
||||
#import <discord_game_sdk.h>
|
||||
#import "discordstate.h"
|
||||
#endif
|
||||
|
||||
#ifdef INI_ENCODING
|
||||
extern "C" {
|
||||
#include <libguess.h>
|
||||
}
|
||||
#include <iconv.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
/* http://stackoverflow.com/a/1031773 */
|
||||
static bool validUtf8(const char *string)
|
||||
{
|
||||
const uint8_t *bytes = (uint8_t*) string;
|
||||
|
||||
while(*bytes)
|
||||
{
|
||||
if( (/* ASCII
|
||||
* use bytes[0] <= 0x7F to allow ASCII control characters */
|
||||
bytes[0] == 0x09 ||
|
||||
bytes[0] == 0x0A ||
|
||||
bytes[0] == 0x0D ||
|
||||
(0x20 <= bytes[0] && bytes[0] <= 0x7E)
|
||||
)
|
||||
) {
|
||||
bytes += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( (/* non-overlong 2-byte */
|
||||
(0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
|
||||
(0x80 <= bytes[1] && bytes[1] <= 0xBF)
|
||||
)
|
||||
) {
|
||||
bytes += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( (/* excluding overlongs */
|
||||
bytes[0] == 0xE0 &&
|
||||
(0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
|
||||
(0x80 <= bytes[2] && bytes[2] <= 0xBF)
|
||||
) ||
|
||||
(/* straight 3-byte */
|
||||
((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
|
||||
bytes[0] == 0xEE ||
|
||||
bytes[0] == 0xEF) &&
|
||||
(0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
|
||||
(0x80 <= bytes[2] && bytes[2] <= 0xBF)
|
||||
) ||
|
||||
(/* excluding surrogates */
|
||||
bytes[0] == 0xED &&
|
||||
(0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
|
||||
(0x80 <= bytes[2] && bytes[2] <= 0xBF)
|
||||
)
|
||||
) {
|
||||
bytes += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( (/* planes 1-3 */
|
||||
bytes[0] == 0xF0 &&
|
||||
(0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
|
||||
(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
|
||||
(0x80 <= bytes[3] && bytes[3] <= 0xBF)
|
||||
) ||
|
||||
(/* planes 4-15 */
|
||||
(0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
|
||||
(0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
|
||||
(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
|
||||
(0x80 <= bytes[3] && bytes[3] <= 0xBF)
|
||||
) ||
|
||||
(/* plane 16 */
|
||||
bytes[0] == 0xF4 &&
|
||||
(0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
|
||||
(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
|
||||
(0x80 <= bytes[3] && bytes[3] <= 0xBF)
|
||||
)
|
||||
) {
|
||||
bytes += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string prefPath(const char *org, const char *app)
|
||||
{
|
||||
char *path = SDL_GetPrefPath(org, app);
|
||||
|
@ -194,6 +108,7 @@ void Config::read(int argc, char *argv[])
|
|||
|
||||
// Not gonna take your shit boost
|
||||
#define GUARD_ALL( exp ) try { exp } catch(...) {}
|
||||
#define GUARD_ALL_OBJ( exp ) @try { exp } @catch(...) {}
|
||||
|
||||
editor.debug = false;
|
||||
editor.battleTest = false;
|
||||
|
@ -318,6 +233,29 @@ void Config::readGameINI()
|
|||
return;
|
||||
}
|
||||
|
||||
OFString* iniFilename = [OFString stringWithFormat:@"%s.ini", execName.c_str()];
|
||||
if ([[OFFileManager defaultManager] fileExistsAtPath:iniFilename])
|
||||
{
|
||||
@try{
|
||||
OFINIFile* iniFile = [OFINIFile fileWithPath:iniFilename];
|
||||
OFINICategory* iniCat = [iniFile categoryForName:@"Game"];
|
||||
GUARD_ALL_OBJ( game.title = [[iniCat stringForKey:@"Title" defaultValue:@""] UTF8String]; )
|
||||
GUARD_ALL_OBJ( game.scripts = [[iniCat stringForKey:@"Scripts" defaultValue:@""] UTF8String]; )
|
||||
|
||||
strReplace(game.scripts, '\\', '/');
|
||||
}
|
||||
@catch(OFException* exc){
|
||||
Debug() << "Failed to parse INI:" << [[exc description] UTF8String];
|
||||
}
|
||||
if (game.title.empty())
|
||||
Debug() << [iniFilename UTF8String] << ": Could not find Game.Title property";
|
||||
|
||||
if (game.scripts.empty())
|
||||
Debug() << [iniFilename UTF8String] << ": Could not find Game.Scripts property";
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
std::string iniFilename = execName + ".ini";
|
||||
SDLRWStream iniFile(iniFilename.c_str(), "r");
|
||||
|
||||
|
@ -350,68 +288,8 @@ void Config::readGameINI()
|
|||
{
|
||||
Debug() << "FAILED to open" << iniFilename;
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef INI_ENCODING
|
||||
/* Can add more later */
|
||||
const char *languages[] =
|
||||
{
|
||||
titleLanguage.c_str(),
|
||||
GUESS_REGION_JP, /* Japanese */
|
||||
GUESS_REGION_KR, /* Korean */
|
||||
GUESS_REGION_CN, /* Chinese */
|
||||
0
|
||||
};
|
||||
|
||||
bool convSuccess = true;
|
||||
|
||||
/* Verify that the game title is UTF-8, and if not,
|
||||
* try to determine the encoding and convert to UTF-8 */
|
||||
if (!validUtf8(game.title.c_str()))
|
||||
{
|
||||
const char *encoding = 0;
|
||||
convSuccess = false;
|
||||
|
||||
for (size_t i = 0; languages[i]; ++i)
|
||||
{
|
||||
encoding = libguess_determine_encoding(game.title.c_str(),
|
||||
game.title.size(),
|
||||
languages[i]);
|
||||
if (encoding)
|
||||
break;
|
||||
}
|
||||
|
||||
if (encoding)
|
||||
{
|
||||
iconv_t cd = iconv_open("UTF-8", encoding);
|
||||
|
||||
size_t inLen = game.title.size();
|
||||
size_t outLen = inLen * 4;
|
||||
std::string buf(outLen, '\0');
|
||||
char *inPtr = const_cast<char*>(game.title.c_str());
|
||||
char *outPtr = const_cast<char*>(buf.c_str());
|
||||
|
||||
errno = 0;
|
||||
size_t result = iconv(cd, &inPtr, &inLen, &outPtr, &outLen);
|
||||
|
||||
iconv_close(cd);
|
||||
|
||||
if (result != (size_t) -1 && errno == 0)
|
||||
{
|
||||
buf.resize(buf.size()-outLen);
|
||||
game.title = buf;
|
||||
convSuccess = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!convSuccess)
|
||||
game.title.clear();
|
||||
#else
|
||||
if (!validUtf8(game.title.c_str()))
|
||||
game.title.clear();
|
||||
#endif
|
||||
|
||||
|
||||
if (game.title.empty())
|
||||
{
|
||||
game.title = "mkxp-z";
|
|
@ -16,7 +16,7 @@ typedef unsigned int DWORD, UINT, *LPDWORD;
|
|||
typedef char BYTE, *LPSTR, *LPCSTR, *LPCTSTR, *LPTSTR, *PBYTE;
|
||||
typedef short SHORT;
|
||||
typedef int LONG;
|
||||
typedef bool BOOL;
|
||||
typedef signed char BOOL;
|
||||
typedef void VOID, *LPVOID, *HANDLE, *HMODULE, *HWND;
|
||||
typedef size_t SIZE_T;
|
||||
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
#include <SDL.h>
|
||||
#import <SDL.h>
|
||||
#ifdef __APPLE__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
#import <ObjFW/ObjFW.h>
|
||||
|
||||
#ifdef __WIN32__
|
||||
#include <windows.h>
|
||||
#include <SDL_syswm.h>
|
||||
#import <windows.h>
|
||||
#import <SDL_syswm.h>
|
||||
#else
|
||||
#include <cstring>
|
||||
#include "iniconfig.h"
|
||||
#import <cstring>
|
||||
#endif
|
||||
|
||||
#include "sharedstate.h"
|
||||
#include "eventthread.h"
|
||||
#include "filesystem.h"
|
||||
#include "input.h"
|
||||
#include "lang-fun.h"
|
||||
#include "fake-api.h"
|
||||
#import "sharedstate.h"
|
||||
#import "eventthread.h"
|
||||
#import "filesystem.h"
|
||||
#import "input.h"
|
||||
#import "lang-fun.h"
|
||||
#import "fake-api.h"
|
||||
|
||||
|
||||
// Essentials, without edits, needs Win32API. Two problems with that:
|
||||
|
@ -405,23 +408,25 @@ MKXP_GetPrivateProfileString(LPCTSTR lpAppName,
|
|||
DWORD nSize,
|
||||
LPCTSTR lpFileName)
|
||||
{
|
||||
char *lpFileName_normal = shState->fileSystem().normalize(lpFileName, true, false);
|
||||
SDLRWStream iniFile(lpFileName_normal, "r");
|
||||
delete lpFileName_normal;
|
||||
if (iniFile)
|
||||
OFString* filePath = [OFString stringWithUTF8String:lpFileName];
|
||||
OFString* ret = 0;
|
||||
if ([[OFFileManager defaultManager] fileExistsAtPath:filePath])
|
||||
{
|
||||
INIConfiguration ic;
|
||||
if (ic.load(iniFile.stream()))
|
||||
{
|
||||
std::string result = ic.getStringProperty(lpAppName, lpKeyName);
|
||||
if (!result.empty())
|
||||
{
|
||||
strncpy(lpReturnedString, result.c_str(), nSize);
|
||||
return result.length();
|
||||
}
|
||||
@try{
|
||||
OFINIFile* iniFile = [OFINIFile fileWithPath:filePath];
|
||||
OFINICategory* iniCat = [iniFile categoryForName:[OFString stringWithUTF8String:lpAppName]];
|
||||
ret = [iniCat stringForKey:[OFString stringWithUTF8String:lpKeyName]];
|
||||
}
|
||||
@catch(...){}
|
||||
}
|
||||
if (ret)
|
||||
{
|
||||
strncpy(lpReturnedString, [ret UTF8String], nSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(lpReturnedString, lpDefault, nSize);
|
||||
}
|
||||
strncpy(lpReturnedString, lpDefault, nSize);
|
||||
return strlen(lpDefault);
|
||||
}
|
||||
|
||||
|
@ -464,9 +469,13 @@ PREFABI BOOL
|
|||
MKXP_GetUserName(LPSTR lpBuffer, LPDWORD pcbBuffer)
|
||||
{
|
||||
if (*pcbBuffer < 1) return false;
|
||||
#ifdef __APPLE__
|
||||
strncpy(lpBuffer, [NSUserName() UTF8String], *pcbBuffer);
|
||||
#else
|
||||
char *username = getenv("USER");
|
||||
strncpy(lpBuffer, (username) ? username : "ditto", *pcbBuffer);
|
||||
lpBuffer[0] = toupper(lpBuffer[0]);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
|
@ -721,10 +721,13 @@ char* FileSystem::normalize(const char *pathname, bool preferred, bool absolute)
|
|||
|
||||
if (absolute)
|
||||
{
|
||||
OFURL* base = [OFURL fileURLWithPath:[[OFFileManager defaultManager] currentDirectoryPath]];
|
||||
OFURL* purl = [OFURL fileURLWithPath:str];
|
||||
OFString* path = [[OFURL URLWithString:[purl string] relativeToURL:base] path];
|
||||
str = [OFMutableString stringWithString:path];
|
||||
@try{
|
||||
OFURL* base = [OFURL fileURLWithPath:[[OFFileManager defaultManager] currentDirectoryPath]];
|
||||
OFURL* purl = [OFURL fileURLWithPath:str];
|
||||
OFString* path = [[OFURL URLWithString:[purl string] relativeToURL:base] path];
|
||||
str = [OFMutableString stringWithString:path];
|
||||
}
|
||||
@catch(...){}
|
||||
}
|
||||
|
||||
#ifdef __WIN32__
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
#include "iniconfig.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
std::string toLowerCase(const std::string& str)
|
||||
{
|
||||
std::string lower = str;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
return lower;
|
||||
}
|
||||
|
||||
std::string trim(const std::string& str, const std::string& chars = "\t\n\v\f\r ")
|
||||
{
|
||||
std::string trimmed = str;
|
||||
trimmed.erase(trimmed.find_last_not_of(chars) + 1);
|
||||
trimmed.erase(0, trimmed.find_first_not_of(chars));
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
INIConfiguration::Section::Section (const std::string& sname) : m_Name (sname), m_PropertyMap()
|
||||
{
|
||||
}
|
||||
|
||||
bool INIConfiguration::Section::getStringProperty (const std::string& name, std::string& outPropStr) const
|
||||
{
|
||||
try
|
||||
{
|
||||
outPropStr = m_PropertyMap.at(toLowerCase(name)).m_Value;
|
||||
return true;
|
||||
}
|
||||
catch (std::out_of_range& oorexcept)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool INIConfiguration::load (std::istream& is)
|
||||
{
|
||||
if (!is.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string currSectionName;
|
||||
|
||||
std::string line;
|
||||
std::getline (is, line);
|
||||
|
||||
while (!is.eof() && !is.bad())
|
||||
{
|
||||
if (line[0] == '[')
|
||||
{
|
||||
currSectionName = line.substr (1, line.find_last_of (']') - 1);
|
||||
}
|
||||
else if (line[0] != '#' && line.length() > 2)
|
||||
{
|
||||
int crloc = line.length() - 1;
|
||||
|
||||
if (crloc >= 0 && line[crloc] == '\r') //check for Windows-style newline
|
||||
line.resize (crloc); //and correct
|
||||
|
||||
size_t equalsPos = line.find_first_of ("=");
|
||||
|
||||
if (equalsPos != std::string::npos)
|
||||
{
|
||||
std::string key = line.substr (0, equalsPos);
|
||||
std::string val = line.substr (equalsPos + 1);
|
||||
|
||||
addProperty (currSectionName, key , val);
|
||||
}
|
||||
}
|
||||
|
||||
std::getline (is, line);
|
||||
}
|
||||
|
||||
if (is.bad())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::string INIConfiguration::getStringProperty(const std::string& sname, const std::string& name, const std::string& def) const
|
||||
{
|
||||
auto sectionIt = m_SectionMap.find(toLowerCase(sname));
|
||||
|
||||
if (sectionIt != m_SectionMap.end())
|
||||
{
|
||||
std::string prop;
|
||||
|
||||
if(sectionIt->second.getStringProperty(name, prop))
|
||||
{
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
void INIConfiguration::addProperty (const std::string& sname, const std::string& name, const std::string& val)
|
||||
{
|
||||
if (m_SectionMap.find (toLowerCase(sname)) == m_SectionMap.end())
|
||||
{
|
||||
m_SectionMap.emplace (toLowerCase(sname), Section (sname));
|
||||
}
|
||||
|
||||
Section::Property p;
|
||||
p.m_Name = trim(name);
|
||||
p.m_Value = trim(val);
|
||||
|
||||
m_SectionMap.at (toLowerCase(sname)).m_PropertyMap[toLowerCase(p.m_Name)] = p;
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
#ifndef INICONFIG_H
|
||||
#define INICONFIG_H
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
class INIConfiguration
|
||||
{
|
||||
class Section
|
||||
{
|
||||
friend class INIConfiguration;
|
||||
|
||||
struct Property
|
||||
{
|
||||
std::string m_Name;
|
||||
std::string m_Value;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, Property> property_map;
|
||||
public:
|
||||
Section (const Section& s) = default;
|
||||
Section (Section&& s) = default;
|
||||
|
||||
bool getStringProperty (const std::string& name, std::string& outPropStr) const;
|
||||
|
||||
private:
|
||||
explicit Section (const std::string& name);
|
||||
|
||||
std::string m_Name;
|
||||
property_map m_PropertyMap;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, Section> section_map;
|
||||
public:
|
||||
bool load (std::istream& inStream);
|
||||
|
||||
std::string getStringProperty(const std::string& sname, const std::string& name, const std::string& def = "") const;
|
||||
|
||||
protected:
|
||||
void addProperty (const std::string& sname, const std::string& name, const std::string& val);
|
||||
|
||||
private:
|
||||
section_map m_SectionMap;
|
||||
};
|
||||
|
||||
#endif // INICONFIG_H
|
|
@ -41,11 +41,11 @@ NSString* get_mac_locale(void)
|
|||
NSString* locale_string;
|
||||
|
||||
if (country != nil)
|
||||
locale_string = [NSString stringWithFormat:@"%@_%@", lang, country];
|
||||
locale_string = [NSString stringWithFormat:[NSString stringWithUTF8String:"%@_%@"], lang, country];
|
||||
else
|
||||
locale_string = [NSString stringWithString:lang];
|
||||
|
||||
[mac_locale appendFormat:@"%@%@", locale_string, @".UTF-8"];
|
||||
[mac_locale appendFormat:[NSString stringWithUTF8String:"%@%@"], locale_string, [NSString stringWithUTF8String:".UTF-8"]];
|
||||
}
|
||||
return mac_locale;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ main_source = files(
|
|||
'filesystem.mm',
|
||||
'font.cpp',
|
||||
'input.cpp',
|
||||
'iniconfig.cpp',
|
||||
'plane.cpp',
|
||||
'scene.cpp',
|
||||
'sprite.cpp',
|
||||
|
@ -63,7 +62,7 @@ main_source = files(
|
|||
'graphics.cpp',
|
||||
'gl-debug.cpp',
|
||||
'etc.cpp',
|
||||
'config.cpp',
|
||||
'config.mm',
|
||||
'settingsmenu.cpp',
|
||||
'keybindings.cpp',
|
||||
'tileatlas.cpp',
|
||||
|
@ -87,7 +86,7 @@ main_source = files(
|
|||
)
|
||||
|
||||
if get_option('use_fakeapi') == true
|
||||
main_source += files('fake-api.cpp')
|
||||
main_source += files('fake-api.mm')
|
||||
endif
|
||||
|
||||
if discord == true
|
||||
|
|
Loading…
Add table
Reference in a new issue