From dad016d6e5f871c37d32109425cc39c71f22d6ba Mon Sep 17 00:00:00 2001 From: zeromus Date: Tue, 26 Jul 2011 22:04:27 +0000 Subject: [PATCH] win32: change compactflash rompath to use the actual rom's path instead of the rom path setting configuration (that isnt what anybody wanted) --- desmume/src/addons/slot2_mpcf.cpp | 33 +++---- desmume/src/path.cpp | 101 +++++++++++++++++++- desmume/src/path.h | 152 ++++++++++++------------------ desmume/src/windows/resources.rc | Bin 1409472 -> 1405976 bytes 4 files changed, 175 insertions(+), 111 deletions(-) diff --git a/desmume/src/addons/slot2_mpcf.cpp b/desmume/src/addons/slot2_mpcf.cpp index 7fb3e03be..217d826c5 100644 --- a/desmume/src/addons/slot2_mpcf.cpp +++ b/desmume/src/addons/slot2_mpcf.cpp @@ -1,22 +1,23 @@ -/* Copyright (C) 2006 yopyop - Copyright (C) 2006 Mic - Copyright (C) 2009-2011 DeSmuME team +/* + Copyright (C) 2006 yopyop + Copyright (C) 2006 Mic + Copyright (C) 2009-2011 DeSmuME team - This file is part of DeSmuME + This file is part of DeSmuME - DeSmuME 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; either version 2 of the License, or - (at your option) any later version. + DeSmuME 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; either version 2 of the License, or + (at your option) any later version. - DeSmuME 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 for more details. + DeSmuME 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 for more details. - You should have received a copy of the GNU General Public License - along with DeSmuME; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU General Public License + along with DeSmuME; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../addons.h" @@ -82,7 +83,7 @@ static BOOL cflash_init() if (CFlash_Mode == ADDON_CFLASH_MODE_RomPath) { - sFlashPath = path.pathToRoms; + sFlashPath = path.RomDirectory; INFO("Using CFlash directory of rom: %s\n", sFlashPath.c_str()); } else if(CFlash_Mode == ADDON_CFLASH_MODE_Path) diff --git a/desmume/src/path.cpp b/desmume/src/path.cpp index 1197d5856..82aa321c1 100644 --- a/desmume/src/path.cpp +++ b/desmume/src/path.cpp @@ -1,4 +1,5 @@ -/* Copyright 2009-2010 DeSmuME team +/* + Copyright 2009-2011 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +21,104 @@ #include +//----------------------------------- +//This is taken from mono Path.cs +static const char InvalidPathChars[] = { + '\x22', '\x3C', '\x3E', '\x7C', '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', + '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12', + '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', + '\x1E', '\x1F' +}; + +//but it is sort of windows-specific. Does it work in linux? Maybe we'll have to make it smarter +static const char VolumeSeparatorChar = ':'; +static const char AltDirectorySeparatorChar = '/'; +static bool dirEqualsVolume = (DIRECTORY_DELIMITER_CHAR == VolumeSeparatorChar); + + +bool Path::IsPathRooted (const std::string &path) +{ + if (path.empty()) { + return false; + } + + if (path.find_first_of(InvalidPathChars) != std::string::npos) { + return false; + } + + char c = path[0]; + return (c == DIRECTORY_DELIMITER_CHAR || + c == AltDirectorySeparatorChar || + (!dirEqualsVolume && path.size() > 1 && path[1] == VolumeSeparatorChar)); +} + +std::string Path::GetFileDirectoryPath(std::string filePath) +{ + if (filePath.empty()) { + return ""; + } + + size_t i = filePath.find_last_of(DIRECTORY_DELIMITER_CHAR); + if (i == std::string::npos) { + return filePath; + } + + return filePath.substr(0, i); +} + +std::string Path::GetFileNameFromPath(std::string filePath) +{ + if (filePath.empty()) { + return ""; + } + + size_t i = filePath.find_last_of(DIRECTORY_DELIMITER_CHAR); + if (i == std::string::npos) { + return filePath; + } + + return filePath.substr(i + 1); +} + +std::string Path::GetFileNameWithoutExt(std::string fileName) +{ + if (fileName.empty()) { + return ""; + } + + size_t i = fileName.find_last_of(FILE_EXT_DELIMITER_CHAR); + if (i == std::string::npos) { + return fileName; + } + + return fileName.substr(0, i); +} + +std::string Path::GetFileNameFromPathWithoutExt(std::string filePath) +{ + if (filePath.empty()) { + return ""; + } + + std::string fileName = GetFileNameFromPath(filePath); + + return GetFileNameWithoutExt(fileName); +} + +std::string Path::GetFileExt(std::string fileName) +{ + if (fileName.empty()) { + return ""; + } + + size_t i = fileName.find_last_of(FILE_EXT_DELIMITER_CHAR); + if (i == std::string::npos) { + return fileName; + } + + return fileName.substr(i + 1); +} + //----------------------------------- #ifdef _WINDOWS void FCEUD_MakePathDirs(const char *fname) diff --git a/desmume/src/path.h b/desmume/src/path.h index f21961510..53e958e82 100644 --- a/desmume/src/path.h +++ b/desmume/src/path.h @@ -1,4 +1,5 @@ -/* Copyright 2009-2010 DeSmuME team +/* + Copyright 2009-2011 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,44 +40,26 @@ #include "utils/xstring.h" #ifdef _WINDOWS -void FCEUD_MakePathDirs(const char *fname); +#define FILE_EXT_DELIMITER_CHAR '.' +#define DIRECTORY_DELIMITER_CHAR '\\' +#else +#define FILE_EXT_DELIMITER_CHAR '.' +#define DIRECTORY_DELIMITER_CHAR '/' #endif -//----------------------------------- -//This is taken from mono Path.cs -static const char InvalidPathChars[] = { - '\x22', '\x3C', '\x3E', '\x7C', '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', - '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12', - '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', - '\x1E', '\x1F' -}; - -//but it is sort of windows-specific. Does it work in linux? Maybe we'll have to make it smarter -static const char VolumeSeparatorChar = ':'; -static const char DirectorySeparatorChar = '\\'; -static const char AltDirectorySeparatorChar = '/'; -static bool dirEqualsVolume = (DirectorySeparatorChar == VolumeSeparatorChar); +#ifdef _WINDOWS +void FCEUD_MakePathDirs(const char *fname); +#endif class Path { public: - static bool IsPathRooted (const std::string& path) - { - if (path.size() == 0) - return false; - - - if (path.find_first_of(InvalidPathChars) != -1) - { - //yuck. - //throw new ArgumentException ("Illegal characters in path."); - } - - char c = path [0]; - return (c == DirectorySeparatorChar || - c == AltDirectorySeparatorChar || - (!dirEqualsVolume && path.size() > 1 && path [1] == VolumeSeparatorChar)); - } + static bool IsPathRooted (const std::string &path); + static std::string GetFileDirectoryPath(std::string filePath); + static std::string GetFileNameFromPath(std::string filePath); + static std::string GetFileNameWithoutExt(std::string fileName); + static std::string GetFileNameFromPathWithoutExt(std::string filePath); + static std::string GetFileExt(std::string fileName); }; class PathInfo @@ -85,6 +68,7 @@ public: std::string path; std::string RomName; + std::string RomDirectory; #define MAX_FORMAT 20 #define SECTION "PathSettings" @@ -132,7 +116,7 @@ public: char pathToModule[MAX_PATH]; char pathToLua[MAX_PATH]; - void init(const char * filename) { + void init(const char *filename) { path = std::string(filename); @@ -155,15 +139,10 @@ public: GetModuleFileName(NULL, pathToModule, sizeof(pathToModule)); p = pathToModule + lstrlen(pathToModule); - while (p >= pathToModule && *p != '\\') p--; + while (p >= pathToModule && *p != DIRECTORY_DELIMITER_CHAR) p--; if (++p >= pathToModule) *p = 0; #elif defined(DESMUME_COCOA) - std::string pathStr = path; - - //Truncate the path from filename - int x = pathStr.find_last_of("/\\"); - if (x > 0) - pathStr = pathStr.substr(0, x); + std::string pathStr = Path::GetFileDirectoryPath(path); strncpy(pathToModule, pathStr.c_str(), MAX_PATH); #else @@ -183,7 +162,7 @@ public: void GetDefaultPath(char *pathToDefault, const char *key, int maxCount) { #ifdef _WINDOWS - std::string temp = (std::string)".\\" + pathToDefault; + std::string temp = (std::string)"." + DIRECTORY_DELIMITER_CHAR + pathToDefault; strncpy(pathToDefault, temp.c_str(), maxCount); #else strncpy(pathToDefault, pathToModule, maxCount); @@ -233,7 +212,7 @@ public: }*/ } - void SwitchPath(Action action, KnownPath path, char * buffer) + void SwitchPath(Action action, KnownPath path, char *buffer) { char *pathToCopy = 0; switch(path) @@ -269,28 +248,23 @@ public: if(action == GET) { - std::string temp = pathToCopy; - int len = (int)temp.size()-1; -#ifdef WIN32 + std::string thePath = pathToCopy; + std::string relativePath = "." + DIRECTORY_DELIMITER_CHAR; + + int len = (int)thePath.size()-1; + if(len == -1) - temp = ".\\"; - else - if(temp[len] != '\\') - temp += "\\"; -#else - if(len == -1) - temp = "./"; + thePath = relativePath; else - if(temp[len] != '/') - temp += "/"; -#endif + if(thePath[len] != DIRECTORY_DELIMITER_CHAR) + thePath += DIRECTORY_DELIMITER_CHAR; - if(!Path::IsPathRooted(temp)) + if(!Path::IsPathRooted(thePath)) { - temp = (std::string)pathToModule + temp; + thePath = (std::string)pathToModule + thePath; } - strncpy(buffer, temp.c_str(), MAX_PATH); + strncpy(buffer, thePath.c_str(), MAX_PATH); #ifdef _WINDOWS FCEUD_MakePathDirs(buffer); #endif @@ -298,7 +272,7 @@ public: else if(action == SET) { int len = strlen(buffer)-1; - if(buffer[len] == '\\') + if(buffer[len] == DIRECTORY_DELIMITER_CHAR) buffer[len] = '\0'; strncpy(pathToCopy, buffer, MAX_PATH); @@ -333,26 +307,16 @@ public: strcat(buffer, GetRomNameWithoutExtension().c_str()); } - std::string extension() { - - for(int i = int(path.size()) - 1; i >= 0; --i) - { - if (path[i] == '.') { - return path.substr(i+1); - } - } - return path; + std::string extension() + { + return Path::GetFileExt(path); } - std::string noextension() { - - for(int i = int(path.size()) - 1; i >= 0; --i) - { - if (path[i] == '.') { - return path.substr(0, i); - } - } - return path; + std::string noextension() + { + std::string romNameWithPath = Path::GetFileDirectoryPath(path) + DIRECTORY_DELIMITER_CHAR + Path::GetFileNameWithoutExt(RomName); + + return romNameWithPath; } void formatname(char *output) @@ -445,13 +409,10 @@ public: void SetRomName(const char *filename) { - std::string str = filename; - - //Truncate the path from filename - int x = str.find_last_of("/\\"); - if (x > 0) - str = str.substr(x+1); - RomName = str; + std::string romPath = filename; + + RomName = Path::GetFileNameFromPath(romPath); + RomDirectory = Path::GetFileDirectoryPath(romPath); } const char *GetRomName() @@ -461,18 +422,21 @@ public: std::string GetRomNameWithoutExtension() { - int x = RomName.find_last_of("."); - if (x > 0) - return RomName.substr(0,x); - else return RomName; + return Path::GetFileNameWithoutExt(RomName); } - bool isdsgba(std::string str) { - int x = str.find_last_of("."); - if (x > 0) - str = str.substr(x-2); - if(!strcmp(str.c_str(), "ds.gba")) + bool isdsgba(std::string fileName) + { + size_t i = fileName.find_last_of(FILE_EXT_DELIMITER_CHAR); + + if (i != std::string::npos) { + fileName = fileName.substr(i - 2); + } + + if(fileName == "ds.gba") { return true; + } + return false; } }; diff --git a/desmume/src/windows/resources.rc b/desmume/src/windows/resources.rc index 9be3235bb233231de4a9a483a40260c9d7cdc9f5..66ad3de9063c42b7913b189906c8d1e2d028da92 100644 GIT binary patch delta 4845 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 dD@of)g3g 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