From 16a06984bf002a77ae997b3f1d4fa7b2d0555187 Mon Sep 17 00:00:00 2001 From: p989 Date: Wed, 16 Dec 2009 04:59:39 +0000 Subject: [PATCH] wx: ini file and control config saving --- desmume/src/wx/IniFile.cpp | 525 +++++++++++++++++++++ desmume/src/wx/IniFile.h | 93 ++++ desmume/src/wx/PadSimple/GUI/ConfigDlg.cpp | 1 + desmume/src/wx/PadSimple/PadSimple.cpp | 17 +- desmume/src/wx/StringUtil.cpp | 506 ++++++++++++++++++++ desmume/src/wx/StringUtil.h | 83 ++++ desmume/src/wx/wxMain.cpp | 12 +- 7 files changed, 1225 insertions(+), 12 deletions(-) create mode 100644 desmume/src/wx/IniFile.cpp create mode 100644 desmume/src/wx/IniFile.h create mode 100644 desmume/src/wx/StringUtil.cpp create mode 100644 desmume/src/wx/StringUtil.h diff --git a/desmume/src/wx/IniFile.cpp b/desmume/src/wx/IniFile.cpp new file mode 100644 index 000000000..62ccaaa3e --- /dev/null +++ b/desmume/src/wx/IniFile.cpp @@ -0,0 +1,525 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +// see IniFile.h + +#include +#include + +#include +#include +#include +#include +#include + +//#include "StringUtil.h" +#include "IniFile.h" + +IniFile::IniFile() +{} + +IniFile::~IniFile() +{} + +Section::Section() + : lines(), name(""), comment("") {} + + +Section::Section(const std::string& _name) + : lines(), name(_name), comment("") {} + + +Section::Section(const Section& other) +{ + name = other.name; + comment = other.comment; + lines = other.lines; +} + +const Section* IniFile::GetSection(const char* sectionName) const +{ + for (std::vector
::const_iterator iter = sections.begin(); iter != sections.end(); ++iter) + if (!strcasecmp(iter->name.c_str(), sectionName)) + return (&(*iter)); + return 0; +} + +Section* IniFile::GetSection(const char* sectionName) +{ + for (std::vector
::iterator iter = sections.begin(); iter != sections.end(); ++iter) + if (!strcasecmp(iter->name.c_str(), sectionName)) + return (&(*iter)); + return 0; +} + +Section* IniFile::GetOrCreateSection(const char* sectionName) +{ + Section* section = GetSection(sectionName); + + if (!section) + { + sections.push_back(Section(sectionName)); + section = §ions[sections.size() - 1]; + } + + return(section); +} + + +bool IniFile::DeleteSection(const char* sectionName) +{ + Section* s = GetSection(sectionName); + + if (!s) + { + return false; + } + + for (std::vector
::iterator iter = sections.begin(); iter != sections.end(); ++iter) + { + if (&(*iter) == s) + { + sections.erase(iter); + return true; + } + } + + return false; +} + +void IniFile::ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut, std::string* commentOut) const +{ + // + int FirstEquals = (int)line.find("=", 0); + int FirstCommentChar = -1; + // Comments + //if (FirstCommentChar < 0) {FirstCommentChar = (int)line.find(";", FirstEquals > 0 ? FirstEquals : 0);} + if (FirstCommentChar < 0) {FirstCommentChar = (int)line.find("#", FirstEquals > 0 ? FirstEquals : 0);} + if (FirstCommentChar < 0) {FirstCommentChar = (int)line.find("//", FirstEquals > 0 ? FirstEquals : 0);} + + // Allow preservation of spacing before comment + if (FirstCommentChar > 0) + { + while (line[FirstCommentChar - 1] == ' ' || line[FirstCommentChar - 1] == 9) // 9 == tab + { + FirstCommentChar--; + } + } + + if ((FirstEquals >= 0) && ((FirstCommentChar < 0) || (FirstEquals < FirstCommentChar))) + { + // Yes, a valid line! + *keyOut = StripSpaces(line.substr(0, FirstEquals)); + if (commentOut) *commentOut = FirstCommentChar > 0 ? line.substr(FirstCommentChar) : std::string(""); + if (valueOut) *valueOut = StripQuotes(StripSpaces(line.substr(FirstEquals + 1, FirstCommentChar - FirstEquals - 1))); + } +} + +std::string* IniFile::GetLine(Section* section, const char* key, std::string* valueOut, std::string* commentOut) +{ + for (std::vector::iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter) + { + std::string& line = *iter; + std::string lineKey; + ParseLine(line, &lineKey, valueOut, commentOut); + + if (!strcasecmp(lineKey.c_str(), key)) + { + return &line; + } + } + + return 0; +} + +bool IniFile::Exists(const char* const sectionName, const char* key) const +{ + + const Section* const section = GetSection(sectionName); + if (!section) + return false; + + for (std::vector::const_iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter) + { + std::string lineKey; + ParseLine(*iter, &lineKey, NULL, NULL); + + if (!strcasecmp(lineKey.c_str(), key)) + { + return true; + } + } + + return false; +} + +void IniFile::SetLines(const char* sectionName, const std::vector &lines) +{ + Section* section = GetOrCreateSection(sectionName); + section->lines.clear(); + + for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) + { + section->lines.push_back(*iter); + } +} + + +bool IniFile::DeleteKey(const char* sectionName, const char* key) +{ + Section* section = GetSection(sectionName); + + if (!section) + { + return false; + } + + std::string* line = GetLine(section, key, 0, 0); + + for (std::vector::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter) + { + if (line == &(*liter)) + { + section->lines.erase(liter); + return true; + } + } + + return false; //shouldn't happen +} + +// Return a list of all keys in a section +bool IniFile::GetKeys(const char* sectionName, std::vector& keys) const +{ + const Section* section = GetSection(sectionName); + + if (!section) + { + return false; + } + + keys.clear(); + + for (std::vector::const_iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter) + { + std::string key; + ParseLine(*liter, &key, 0, 0); + keys.push_back(key); + } + + return true; +} + +// Return a list of all lines in a section +bool IniFile::GetLines(const char* sectionName, std::vector& lines) const +{ + const Section* section = GetSection(sectionName); + if (!section) + return false; + + lines.clear(); + for (std::vector::const_iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter) + { + std::string line = StripSpaces(*iter); + int commentPos = (int)line.find('#'); + if (commentPos == 0) + { + continue; + } + + if (commentPos != (int)std::string::npos) + { + line = StripSpaces(line.substr(0, commentPos)); + } + + lines.push_back(line); + } + + return true; +} + + +void IniFile::SortSections() +{ + std::sort(sections.begin(), sections.end()); +} + +bool IniFile::Load(const char* filename) +{ + // Maximum number of letters in a line + static const int MAX_BYTES = 1024*32; + + sections.clear(); + sections.push_back(Section("")); + // first section consists of the comments before the first real section + + // Open file + std::ifstream in; + in.open(filename, std::ios::in); + + if (in.fail()) return false; + + while (!in.eof()) + { + char templine[MAX_BYTES]; + in.getline(templine, MAX_BYTES); + std::string line = templine; + +#ifndef _WIN32 + // Check for CRLF eol and convert it to LF + if (!line.empty() && line.at(line.size()-1) == '\r') + { + line.erase(line.size()-1); + } +#endif + + if (in.eof()) break; + + if (line.size() > 0) + { + if (line[0] == '[') + { + size_t endpos = line.find("]"); + + if (endpos != std::string::npos) + { + // New section! + std::string sub = line.substr(1, endpos - 1); + sections.push_back(Section(sub)); + + if (endpos + 1 < line.size()) + { + sections[sections.size() - 1].comment = line.substr(endpos + 1); + } + } + } + else + { + sections[sections.size() - 1].lines.push_back(line); + } + } + } + + in.close(); + return true; +} + +bool IniFile::Save(const char* filename) +{ + std::ofstream out; + out.open(filename, std::ios::out); + + if (out.fail()) + { + return false; + } + + for (std::vector
::const_iterator iter = sections.begin(); iter != sections.end(); ++iter) + { + const Section& section = *iter; + + if (section.name != "") + { + out << "[" << section.name << "]" << section.comment << std::endl; + } + + for (std::vector::const_iterator liter = section.lines.begin(); liter != section.lines.end(); ++liter) + { + std::string s = *liter; + out << s << std::endl; + } + } + + out.close(); + return true; +} + +void IniFile::Set(const char* sectionName, const char* key, const char* newValue) +{ + Section* section = GetOrCreateSection(sectionName); + std::string value, comment; + std::string* line = GetLine(section, key, &value, &comment); + + if (line) + { + // Change the value - keep the key and comment + *line = StripSpaces(key) + " = " + newValue + comment; + } + else + { + // The key did not already exist in this section - let's add it. + section->lines.push_back(std::string(key) + " = " + newValue); + } +} + +void IniFile::Set(const char* sectionName, const char* key, const std::vector& newValues) +{ + std::string temp; + + // Join the strings with , + std::vector::const_iterator it; + for (it = newValues.begin(); it != newValues.end(); ++it) { + + temp = (*it) + ","; + } + + // remove last , + temp.resize(temp.length() - 1); + + Set(sectionName, key, temp.c_str()); +} + +void IniFile::Set(const char* sectionName, const char* key, u32 newValue) +{ + Set(sectionName, key, StringFromFormat("0x%08x", newValue).c_str()); +} + + +void IniFile::Set(const char* sectionName, const char* key, int newValue) +{ + Set(sectionName, key, StringFromInt(newValue).c_str()); +} + + +void IniFile::Set(const char* sectionName, const char* key, bool newValue) +{ + Set(sectionName, key, StringFromBool(newValue).c_str()); +} + +bool IniFile::Get(const char* sectionName, const char* key, std::string* value, const char* defaultValue) +{ + Section* section = GetSection(sectionName); + + if (!section) + { + if (defaultValue) + { + *value = defaultValue; + } + return false; + } + + std::string* line = GetLine(section, key, value, 0); + + if (!line) + { + if (defaultValue) + { + *value = defaultValue; + } + return false; + } + + return true; +} + + +bool IniFile::Get(const char* sectionName, const char* key, std::vector& values) +{ + + std::string temp; + bool retval = Get(sectionName, key, &temp, 0); + + if (! retval || temp.empty()) { + return false; + } + + + // ignore starting , if any + size_t subStart = temp.find_first_not_of(","); + size_t subEnd; + + // split by , + while (subStart != std::string::npos) { + + // Find next , + subEnd = temp.find_first_of(",", subStart); + if (subStart != subEnd) + // take from first char until next , + values.push_back(StripSpaces(temp.substr(subStart, subEnd - subStart))); + + // Find the next non , char + subStart = temp.find_first_not_of(",", subEnd); + } + + return true; +} + +bool IniFile::Get(const char* sectionName, const char* key, int* value, int defaultValue) +{ + std::string temp; + bool retval = Get(sectionName, key, &temp, 0); + + if (retval && TryParseInt(temp.c_str(), value)) + { + return true; + } + + *value = defaultValue; + return false; +} + + +bool IniFile::Get(const char* sectionName, const char* key, u32* value, u32 defaultValue) +{ + std::string temp; + bool retval = Get(sectionName, key, &temp, 0); + + if (retval && TryParseUInt(temp.c_str(), value)) + { + return true; + } + + *value = defaultValue; + return false; +} + + +bool IniFile::Get(const char* sectionName, const char* key, bool* value, bool defaultValue) +{ + std::string temp; + bool retval = Get(sectionName, key, &temp, 0); + + if (retval && TryParseBool(temp.c_str(), value)) + { + return true; + } + + *value = defaultValue; + return false; +} + + +// TODO: Keep this code below? +/* + int main() + { + IniFile ini; + ini.Load("my.ini"); + ini.Set("Hej", "A", "amaskdfl"); + ini.Set("Mossa", "A", "amaskdfl"); + ini.Set("Aissa", "A", "amaskdfl"); + //ini.Read("my.ini"); + std::string x; + ini.Get("Hej", "B", &x, "boo"); + ini.DeleteKey("Mossa", "A"); + ini.DeleteSection("Mossa"); + ini.SortSections(); + ini.Save("my.ini"); + //UpdateVars(ini); + return 0; + } + */ diff --git a/desmume/src/wx/IniFile.h b/desmume/src/wx/IniFile.h new file mode 100644 index 000000000..975fd47ab --- /dev/null +++ b/desmume/src/wx/IniFile.h @@ -0,0 +1,93 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _INIFILE_H_ +#define _INIFILE_H_ + +#include +#include + +#include "types.h" + +#include "StringUtil.h" + +class Section +{ +public: + Section(); + Section(const std::string& _name); + Section(const Section& other); + std::vectorlines; + std::string name; + std::string comment; + + bool operator<(const Section& other) const + { + return(name < other.name); + } +}; + +class IniFile +{ +public: + IniFile(); + ~IniFile(); + + bool Load(const char* filename); + bool Save(const char* filename); + + void Set(const char* sectionName, const char* key, const char* newValue); + void Set(const char* sectionName, const char* key, int newValue); + void Set(const char* sectionName, const char* key, u32 newValue); + void Set(const char* sectionName, const char* key, bool newValue); + void Set(const char* sectionName, const char* key, const std::string& newValue) {Set(sectionName, key, newValue.c_str());} + void Set(const char* sectionName, const char* key, const std::vector& newValues); + + void SetLines(const char* sectionName, const std::vector &lines); + + // Returns true if exists key in section + bool Exists(const char* sectionName, const char* key) const; + + // getter should be const + bool Get(const char* sectionName, const char* key, std::string* value, const char* defaultValue = ""); + bool Get(const char* sectionName, const char* key, int* value, int defaultValue = 0); + bool Get(const char* sectionName, const char* key, u32* value, u32 defaultValue = 0); + bool Get(const char* sectionName, const char* key, bool* value, bool defaultValue = false); + bool Get(const char* sectionName, const char* key, std::vector& values); + + bool GetKeys(const char* sectionName, std::vector& keys) const; + bool GetLines(const char* sectionName, std::vector& lines) const; + + bool DeleteKey(const char* sectionName, const char* key); + bool DeleteSection(const char* sectionName); + + void SortSections(); + + void ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut, std::string* commentOut) const; + std::string* GetLine(Section* section, const char* key, std::string* valueOut, std::string* commentOut); + +private: + std::vector
sections; + + const Section* GetSection(const char* section) const; + Section* GetSection(const char* section); + Section* GetOrCreateSection(const char* section); + std::string* GetLine(const char* section, const char* key); + void CreateSection(const char* section); +}; + +#endif // _INIFILE_H_ diff --git a/desmume/src/wx/PadSimple/GUI/ConfigDlg.cpp b/desmume/src/wx/PadSimple/GUI/ConfigDlg.cpp index 207255a04..8087d1784 100644 --- a/desmume/src/wx/PadSimple/GUI/ConfigDlg.cpp +++ b/desmume/src/wx/PadSimple/GUI/ConfigDlg.cpp @@ -401,6 +401,7 @@ void PADConfigDialogSimple::OnClose(wxCloseEvent& event) void PADConfigDialogSimple::OnCloseClick(wxCommandEvent& event) { + SaveConfig(); Close(); } diff --git a/desmume/src/wx/PadSimple/PadSimple.cpp b/desmume/src/wx/PadSimple/PadSimple.cpp index 2da512dcb..705f6f2cc 100644 --- a/desmume/src/wx/PadSimple/PadSimple.cpp +++ b/desmume/src/wx/PadSimple/PadSimple.cpp @@ -25,7 +25,7 @@ //#include "LogManager.h" #include "pluginspecs_pad.h" #include "PadSimple.h" -//#include "IniFile.h" +#include "../IniFile.h" //#include "StringUtil.h" //#include "FileUtil.h" //#include "ChunkFile.h" @@ -822,7 +822,7 @@ void PAD_Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength) //****************************************************************************** // Load and save the configuration //****************************************************************************** - +extern std::string executableDirectory; void LoadConfig() { // Initialize first pad to standard controls @@ -916,10 +916,9 @@ void LoadConfig() 46, // Mic (m) }; #endif - //TODO -#if 0 IniFile file; - file.Load(FULL_CONFIG_DIR "pad.ini"); + + file.Load((executableDirectory + std::string("pad.ini")).c_str()); for(int i = 0; i < 4; i++) { @@ -953,16 +952,13 @@ void LoadConfig() #endif } } -#endif } void SaveConfig() { - //TODO -#if 0 IniFile file; - file.Load(FULL_CONFIG_DIR "pad.ini"); + file.Load((executableDirectory + std::string("pad.ini")).c_str()); for(int i = 0; i < 4; i++) { @@ -989,6 +985,5 @@ void SaveConfig() file.Set(SectionName, controlNames[x], pad[i].keyForControl[x]); } } - file.Save(FULL_CONFIG_DIR "pad.ini"); -#endif + file.Save((executableDirectory + std::string("pad.ini")).c_str()); } diff --git a/desmume/src/wx/StringUtil.cpp b/desmume/src/wx/StringUtil.cpp new file mode 100644 index 000000000..da4b77ca6 --- /dev/null +++ b/desmume/src/wx/StringUtil.cpp @@ -0,0 +1,506 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "StringUtil.h" + +// faster than sscanf +bool AsciiToHex(const char* _szValue, u32& result) +{ + u32 value = 0; + size_t finish = strlen(_szValue); + + if (finish > 8) + finish = 8; // Max 32-bit values are supported. + + for (size_t count = 0; count < finish; count++) + { + value <<= 4; + switch (_szValue[count]) + { + case '0': break; + case '1': value += 1; break; + case '2': value += 2; break; + case '3': value += 3; break; + case '4': value += 4; break; + case '5': value += 5; break; + case '6': value += 6; break; + case '7': value += 7; break; + case '8': value += 8; break; + case '9': value += 9; break; + case 'A': + case 'a': value += 10; break; + case 'B': + case 'b': value += 11; break; + case 'C': + case 'c': value += 12; break; + case 'D': + case 'd': value += 13; break; + case 'E': + case 'e': value += 14; break; + case 'F': + case 'f': value += 15; break; + default: + return false; + break; + } + } + + result = value; + return (true); +} + +// Convert AB to it's ascii table entry numbers 0x4142 +u32 Ascii2Hex(std::string _Text) +{ + // Reset the return value zero + u32 Result = 0; + + // Max 32-bit values are supported + size_t Length = _Text.length(); + if (Length > 4) + Length = 4; + + for (int i = 0; i < (int)Length; i++) + { + // Add up the values, for example RSPE becomes, 0x52000000, then 0x52530000 and so on + Result += _Text.c_str()[i] << ((Length - 1 - i) * 8); + } + // Return the value + return Result; +} + +// Convert it back again +std::string Hex2Ascii(u32 _Text) +{ + // Create temporary storate + char Result[5]; // need space for the final \0 + // Go through the four characters + sprintf(Result, "%c%c%c%c", _Text >> 24, _Text >> 16, _Text >> 8, _Text); + // Return the string + std::string StrResult = Result; + return StrResult; +} + +bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args) +{ + int writtenCount = vsnprintf(out, outsize, format, args); + + if (writtenCount > 0 && writtenCount < outsize) + { + out[writtenCount] = '\0'; + return true; + } + else + { + out[outsize - 1] = '\0'; + return false; + } +} + +// Expensive! +void ToStringFromFormat(std::string* out, const char* format, ...) +{ + int writtenCount = -1; + int newSize = (int)strlen(format) + 4; + char *buf = 0; + va_list args; + while (writtenCount < 0) + { + delete [] buf; + buf = new char[newSize + 1]; + + va_start(args, format); + writtenCount = vsnprintf(buf, newSize, format, args); + va_end(args); + if (writtenCount >= (int)newSize) { + writtenCount = -1; + } + // ARGH! vsnprintf does no longer return -1 on truncation in newer libc! + // WORKAROUND! let's fake the old behaviour (even though it's less efficient). + // TODO: figure out why the fix causes an invalid read in strlen called from vsnprintf :( +// if (writtenCount >= (int)newSize) +// writtenCount = -1; + newSize *= 2; + } + + buf[writtenCount] = '\0'; + *out = buf; + delete[] buf; +} + + +std::string StringFromFormat(const char* format, ...) +{ + int writtenCount = -1; + int newSize = (int)strlen(format) + 4; + char *buf = 0; + va_list args; + while (writtenCount < 0) + { + delete [] buf; + buf = new char[newSize + 1]; + + va_start(args, format); + writtenCount = vsnprintf(buf, newSize, format, args); + va_end(args); + if (writtenCount >= (int)newSize) { + writtenCount = -1; + } + // ARGH! vsnprintf does no longer return -1 on truncation in newer libc! + // WORKAROUND! let's fake the old behaviour (even though it's less efficient). + // TODO: figure out why the fix causes an invalid read in strlen called from vsnprintf :( +// if (writtenCount >= (int)newSize) +// writtenCount = -1; + newSize *= 2; + } + + buf[writtenCount] = '\0'; + std::string temp = buf; + delete[] buf; + return temp; +} + + +// For Debugging. Read out an u8 array. +std::string ArrayToString(const u8 *data, u32 size, u32 offset, int line_len, bool Spaces) +{ + std::string Tmp, Spc; + if (Spaces) Spc = " "; else Spc = ""; + for (u32 i = 0; i < size; i++) + { + Tmp += StringFromFormat("%02x%s", data[i + offset], Spc.c_str()); + if(i > 1 && (i + 1) % line_len == 0) Tmp.append("\n"); // break long lines + } + return Tmp; +} + +// Turns " hej " into "hej". Also handles tabs. +std::string StripSpaces(const std::string &str) +{ + std::string s = str; + int i; + for (i = 0; i < (int)s.size(); i++) + { + if ((s[i] != ' ') && (s[i] != 9)) + { + break; + } + } + + s = s.substr(i); + + for (i = (int)s.size() - 1; i > 0; i--) + { + if ((s[i] != ' ') && (s[i] != 9)) + { + break; + } + } + + return s.substr(0, i + 1); +} + + +// "\"hello\"" is turned to "hello" +// This one assumes that the string has already been space stripped in both +// ends, as done by StripSpaces above, for example. +std::string StripQuotes(const std::string& s) +{ + if ((s[0] == '\"') && (s[s.size() - 1] == '\"')) + return s.substr(1, s.size() - 2); + else + return s; +} + +// "\"hello\"" is turned to "hello" +// This one assumes that the string has already been space stripped in both +// ends, as done by StripSpaces above, for example. +std::string StripNewline(const std::string& s) +{ + if (!s.size()) + return s; + else if (s[s.size() - 1] == '\n') + return s.substr(0, s.size() - 1); + else + return s; +} + +bool TryParseInt(const char* str, int* outVal) +{ + const char* s = str; + int value = 0; + bool negativ = false; + + if (*s == '-') + { + negativ = true; + s++; + } + + while (*s) + { + char c = *s++; + + if ((c < '0') || (c > '9')) + { + return false; + } + + value = value * 10 + (c - '0'); + } + if (negativ) + value = -value; + + *outVal = value; + return true; +} + + +bool TryParseBool(const char* str, bool* output) +{ + if ((str[0] == '1') || !strcmp(str, "true") || !strcmp(str, "True") || !strcmp(str, "TRUE")) + { + *output = true; + return true; + } + else if (str[0] == '0' || !strcmp(str, "false") || !strcmp(str, "False") || !strcmp(str, "FALSE")) + { + *output = false; + return true; + } + return false; +} + +std::string StringFromInt(int value) +{ + char temp[16]; + sprintf(temp, "%i", value); + return std::string(temp); +} + +std::string StringFromBool(bool value) +{ + return value ? "True" : "False"; +} + +#ifdef _WIN32 +bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension) +{ + char drive[_MAX_DRIVE]; + char dir[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ext[_MAX_EXT]; + + if (_splitpath_s(full_path.c_str(), drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT) == 0) + { + if (_pPath) + { + *_pPath = std::string(drive) + std::string(dir); + } + + if (_pFilename != 0) + { + *_pFilename = fname; + } + + if (_pExtension != 0) + { + *_pExtension = ext; + } + + return true; + } + + return false; +} + + +#else +bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension) +{ + size_t last_slash = full_path.rfind('/'); + + if (last_slash == std::string::npos) + { + return false; // FIXME return the filename + } + + size_t last_dot = full_path.rfind('.'); + + if ((last_dot == std::string::npos) || (last_dot < last_slash)) + { + return false; // FIXME why missing . is critical? + } + + if (_pPath) + { + *_pPath = full_path.substr(0, last_slash + 1); + } + + if (_pFilename) + { + *_pFilename = full_path.substr(last_slash + 1, last_dot - (last_slash + 1)); + } + + if (_pExtension) + { + *_pExtension = full_path.substr(last_dot + 1); + _pExtension->insert(0, "."); + } + else if (_pFilename) + { + *_pFilename += full_path.substr(last_dot); + } + + return true; +} +#endif +#define DIR_SEP "/" +#define DIR_SEP_CHR '/' + +void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename) +{ + _CompleteFilename = _Path; + + // check for seperator + if (_CompleteFilename[_CompleteFilename.size() - 1] != DIR_SEP_CHR) + { +#ifdef _WIN32 + if (_CompleteFilename[_CompleteFilename.size() - 1] != '\\') +#endif + _CompleteFilename += DIR_SEP_CHR; + } + + // add the filename + _CompleteFilename += _Filename; +} + + +void SplitString(const std::string& str, const std::string& delim, std::vector& output) +{ + output.clear(); + + size_t offset = 0; + size_t delimIndex = 0; + + delimIndex = str.find(delim, offset); + + while (delimIndex != std::string::npos) + { + output.push_back(str.substr(offset, delimIndex - offset)); + offset += delimIndex - offset + delim.length(); + delimIndex = str.find(delim, offset); + } + + output.push_back(str.substr(offset)); +} + + +bool TryParseUInt(const std::string& str, u32* output) +{ + if (!strcmp(str.substr(0, 2).c_str(), "0x") || !strcmp(str.substr(0, 2).c_str(), "0X")) + return sscanf(str.c_str() + 2, "%x", output) > 0; + else + return sscanf(str.c_str(), "%d", output) > 0; +} + + +int ChooseStringFrom(const char* str, const char* * items) +{ + int i = 0; + while (items[i] != 0) + { + if (!strcmp(str, items[i])) + return i; + i++; + } + return -1; +} + + +// Thousand separator. Turns 12345678 into 12,345,678 +std::string ThS(int Integer, bool Unsigned, int Spaces) +{ + // Create storage space + char cbuf[20]; + // Determine treatment of signed or unsigned + if(Unsigned) sprintf(cbuf, "%u", Integer); else sprintf(cbuf, "%i", Integer); + + std::string Sbuf = cbuf; + for (u32 i = 0; i < Sbuf.length(); ++i) + { + if((i & 3) == 3) + { + Sbuf.insert(Sbuf.length() - i, ","); + } + } + + // Spaces + std::string Spc = ""; + for (int i = 0; i < (int)(Spaces - Sbuf.length()); i++) Spc += " "; + return Spc + Sbuf; +} + +void NormalizeDirSep(std::string* str) +{ +#ifdef _WIN32 + int i; + while ((i = (int)str->find_first_of('\\')) >= 0) + { + str->replace(i, 1, DIR_SEP); + } +#endif +} + +std::string TabsToSpaces(int tab_size, const std::string &in) +{ + std::string out; + int len = 0; + // First, compute the size of the new string. + for (unsigned i = 0; i < in.size(); i++) + { + if (in[i] == '\t') + len += tab_size; + else + len += 1; + } + out.resize(len); + int out_ctr = 0; + for (unsigned i = 0; i < in.size(); i++) + { + if (in[i] == '\t') + { + for (int j = 0; j < tab_size; j++) + out[out_ctr++] = ' '; + } + else + { + out[out_ctr++] = in[i]; + } + } + return out; +} + +std::string PathToFilename(std::string Path) +{ + std::string Name, Ending; + SplitPath(Path, 0, &Name, &Ending); + return Name + Ending; +} diff --git a/desmume/src/wx/StringUtil.h b/desmume/src/wx/StringUtil.h new file mode 100644 index 000000000..3911455ee --- /dev/null +++ b/desmume/src/wx/StringUtil.h @@ -0,0 +1,83 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _STRINGUTIL_H_ +#define _STRINGUTIL_H_ + +#include + +#include +#include + +#include "Common.h" + +std::string StringFromFormat(const char* format, ...); +void ToStringFromFormat(std::string* out, const char* format, ...); + +// WARNING - only call once with a set of args! +void StringFromFormatV(std::string* out, const char* format, va_list args); +// Cheap! +bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args); + +// Good +std::string ArrayToString(const u8 *data, u32 size, u32 offset = 0, int line_len = 20, bool Spaces = true); + + +template +inline void CharArrayFromFormat(char (& out)[Count], const char* format, ...) +{ + va_list args; + va_start(args, format); + CharArrayFromFormatV(out, Count, format, args); + va_end(args); +} + + +std::string StripSpaces(const std::string &s); +std::string StripQuotes(const std::string &s); +std::string StripNewline(const std::string &s); +// Thousand separator. Turns 12345678 into 12,345,678 +std::string ThS(int a, bool b = true, int Spaces = 0); + +std::string StringFromInt(int value); +std::string StringFromBool(bool value); + +bool TryParseInt(const char* str, int* outVal); +bool TryParseBool(const char* str, bool* output); +bool TryParseUInt(const std::string& str, u32* output); + + +// TODO: kill this +bool AsciiToHex(const char* _szValue, u32& result); +u32 Ascii2Hex(std::string _Text); +std::string Hex2Ascii(u32 _Text); + +std::string TabsToSpaces(int tab_size, const std::string &in); + +void SplitString(const std::string& str, const std::string& delim, std::vector& output); +int ChooseStringFrom(const char* str, const char* * items); + + +// "C:\Windows\winhelp.exe" to "C:\Windows\", "winhelp", "exe" +bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension); +// "C:\Windows\winhelp.exe" to "winhelp.exe" +std::string PathToFilename(std::string Path); + +void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename); +void NormalizeDirSep(std::string* str); + +#endif // _STRINGUTIL_H_ diff --git a/desmume/src/wx/wxMain.cpp b/desmume/src/wx/wxMain.cpp index 6fc258e4d..c290482c6 100644 --- a/desmume/src/wx/wxMain.cpp +++ b/desmume/src/wx/wxMain.cpp @@ -23,6 +23,8 @@ #include "wx/wx.h" #endif +std::string executableDirectory; + class Desmume: public wxApp { public: @@ -409,6 +411,14 @@ bool Desmume::OnInit() DesmumeFrame *frame = new DesmumeFrame((char*)EMU_DESMUME_NAME_AND_VERSION()); frame->Show(true); + char *p, *a; + std::string b = wxStandardPaths::Get().GetExecutablePath(); + a = const_cast(b.c_str()); + p = a + lstrlen(a); + while (p >= a && *p != '\\') p--; + if (++p >= a) *p = 0; + + executableDirectory = std::string(a); SPADInitialize PADInitialize; PADInitialize.padNumber = 1; extern void Initialize(void *init); @@ -425,7 +435,7 @@ DesmumeFrame::DesmumeFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) { - this->SetSize(256,384+46);//why 46? + this->SetClientSize(256,384); wxMenu *fileMenu = new wxMenu; wxMenu *emulationMenu = new wxMenu;