diff --git a/readme.txt b/readme.txt index 3ad52b19..9749a1a7 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ bsnes -Version: 0.035 +Version: 0.036 Author: byuu ======== @@ -38,9 +38,9 @@ If you wish to have multiple configuration profiles for the same user, you will need to make copies of the bsnes executable, and use each one in single-user mode. -================== -Known Limitations: -================== +==================== +Known Limitation(s): +==================== S-CPU - Multiply / divide register delays not implemented @@ -60,6 +60,16 @@ Hardware Bugs - S-CPU.r1 HDMA crashing bug not emulated - S-CPU<>S-SMP communication bus conflicts not emulated +=============== +Known Issue(s): +=============== + +On Windows, attempting to load a ZIP, GZ or JMA compressed archive with +non-ANSI characters in the filename will fail. This is because Windows +requires UTF-16 encoding, but these libraries only work with UTF-8. +Note that loading uncompressed images (SMC, SFC, etc) with non-ANSI characters +works properly on all platforms. + ===================== Unsupported Hardware: ===================== @@ -89,9 +99,9 @@ SETA RISC CPU used by Quick-move Shogi Match with Nidan Rank-holder Morita 2 Super Gameboy Cartridge passthrough used for playing Gameboy games -======================== -Unsupported Controllers: -======================== +========================== +Unsupported Controller(s): +========================== Mouse Super Scope diff --git a/src/base.h b/src/base.h index ccfdd809..fa653c3c 100644 --- a/src/base.h +++ b/src/base.h @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.035" +#define BSNES_VERSION "0.036" #define BSNES_TITLE "bsnes v" BSNES_VERSION #define BUSCORE sBus @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -41,3 +42,9 @@ void alert(const char*, ...); void dprintf(const char*, ...); #include "interface.h" + +//helper: disable access to FILE, when possible (GZIP / JMA require it) +//reason: Windows fopen() does not support UTF-8 filenames; use nall::file instead. +#if !defined(GZIP_SUPPORT) && !defined(JMA_SUPPORT) +#define FILE FILE_deprecated +#endif diff --git a/src/cart/cart.cpp b/src/cart/cart.cpp index 9f5d66e0..d50cd4f4 100644 --- a/src/cart/cart.cpp +++ b/src/cart/cart.cpp @@ -89,7 +89,7 @@ void Cartridge::load_end() { memory::stBrom.write_protect(true); memory::stBram.write_protect(false); - if(fexists(get_cheat_filename(cart.fn, "cht"))) { + if(file::exists(get_cheat_filename(cart.fn, "cht"))) { cheat.clear(); cheat.load(cheatfn); } @@ -122,7 +122,7 @@ bool Cartridge::unload() { char fn[PATH_MAX]; strcpy(fn, cart.fn); modify_extension(fn, "cht"); - if(cheat.count() > 0 || fexists(get_cheat_filename(cart.fn, "cht"))) { + if(cheat.count() > 0 || file::exists(get_cheat_filename(cart.fn, "cht"))) { cheat.save(cheatfn); cheat.clear(); } diff --git a/src/cart/cart_file.cpp b/src/cart/cart_file.cpp index 4e50dd5a..ae0e5e83 100644 --- a/src/cart/cart_file.cpp +++ b/src/cart/cart_file.cpp @@ -1,27 +1,27 @@ #ifdef CART_CPP -#include "../reader/filereader.h" - -#if defined(GZIP_SUPPORT) - #include "../reader/gzreader.h" - #include "../reader/zipreader.h" -#endif - -#if defined(JMA_SUPPORT) - #include "../reader/jmareader.h" -#endif - -char* Cartridge::modify_extension(char *filename, const char *extension) { - int i; - for(i = strlen(filename); i >= 0; i--) { - if(filename[i] == '.') break; - if(filename[i] == '/') break; - if(filename[i] == '\\') break; - } - if(i > 0 && filename[i] == '.') filename[i] = 0; - strcat(filename, "."); - strcat(filename, extension); - return filename; +#include "../reader/filereader.h" + +#if defined(GZIP_SUPPORT) + #include "../reader/gzreader.h" + #include "../reader/zipreader.h" +#endif + +#if defined(JMA_SUPPORT) + #include "../reader/jmareader.h" +#endif + +char* Cartridge::modify_extension(char *filename, const char *extension) { + int i; + for(i = strlen(filename); i >= 0; i--) { + if(filename[i] == '.') break; + if(filename[i] == '/') break; + if(filename[i] == '\\') break; + } + if(i > 0 && filename[i] == '.') filename[i] = 0; + strcat(filename, "."); + strcat(filename, extension); + return filename; } //remove directory information and file extension ("/foo/bar.ext" -> "bar") @@ -46,39 +46,39 @@ char* Cartridge::get_base_filename(char *filename) { } break; } - } - + } + return filename; -} - -char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) { - strcpy(filename, source); - for(char *p = filename; *p; p++) { if(*p == '\\') *p = '/'; } +} + +char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) { + strcpy(filename, source); + for(char *p = filename; *p; p++) { if(*p == '\\') *p = '/'; } modify_extension(filename, extension); - - //override path with user-specified folder, if one was defined + + //override path with user-specified folder, if one was defined if(*path) { - lstring part; - split(part, "/", filename); - string fn = path; - if(strend(fn, "/") == false) strcat(fn, "/"); - strcat(fn, part[count(part) - 1]); - strcpy(filename, fn); - - //resolve relative path, if found - if(strbegin(fn, "./") == true) { - ltrim(fn, "./"); - strcpy(filename, config::path.base); - strcat(filename, fn); - } - } - - return filename; + lstring part; + split(part, "/", filename); + string fn = path; + if(strend(fn, "/") == false) strcat(fn, "/"); + strcat(fn, part[count(part) - 1]); + strcpy(filename, fn); + + //resolve relative path, if found + if(strbegin(fn, "./") == true) { + ltrim(fn, "./"); + strcpy(filename, config::path.base); + strcat(filename, fn); + } + } + + return filename; } char* Cartridge::get_patch_filename(const char *source, const char *extension) { return get_path_filename(patchfn, config::path.patch, source, extension); -} +} char* Cartridge::get_save_filename(const char *source, const char *extension) { return get_path_filename(savefn, config::path.save, source, extension); @@ -87,61 +87,65 @@ char* Cartridge::get_save_filename(const char *source, const char *extension) { char* Cartridge::get_cheat_filename(const char *source, const char *extension) { return get_path_filename(cheatfn, config::path.cheat, source, extension); } - -bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression) { - if(fexists(fn) == false) return false; + +bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression) { + if(file::exists(fn) == false) return false; Reader::Type filetype = Reader::Normal; if(compression == CompressionInspect) filetype = Reader::detect(fn, true); if(compression == CompressionAuto) filetype = Reader::detect(fn, config::file.autodetect_type); - - switch(filetype) { + + switch(filetype) { default: dprintf("* Warning: filetype detected as unsupported compression type."); - dprintf("* Will attempt to load as uncompressed file -- may fail."); - case Reader::Normal: { - FileReader ff(fn); - if(!ff.ready()) { - alert("Error loading image file (%s)!", fn); - return false; - } - size = ff.size(); - data = ff.read(); - } break; - - #ifdef GZIP_SUPPORT - case Reader::GZIP: { - GZReader gf(fn); - if(!gf.ready()) { - alert("Error loading image file (%s)!", fn); - return false; - } - size = gf.size(); - data = gf.read(); - } break; - - case Reader::ZIP: { - ZipReader zf(fn); - size = zf.size(); - data = zf.read(); - } break; - #endif - - #ifdef JMA_SUPPORT - case Reader::JMA: { - try { - JMAReader jf(fn); - size = jf.size(); - data = jf.read(); - } catch(JMA::jma_errors jma_error) { - alert("Error loading image file (%s)!", fn); - return false; - } - } break; - #endif - } - - return true; + dprintf("* Will attempt to load as uncompressed file -- may fail."); + case Reader::Normal: { + FileReader ff(fn); + if(!ff.ready()) { + alert("Error loading image file (%s)!", fn); + return false; + } + size = ff.size(); + data = ff.read(); + } break; + + #ifdef GZIP_SUPPORT + case Reader::GZIP: { + GZReader gf(fn); + if(!gf.ready()) { + alert("Error loading image file (%s)!", fn); + return false; + } + size = gf.size(); + data = gf.read(); + } break; + + case Reader::ZIP: { + ZipReader zf(fn); + if(!zf.ready()) { + alert("Error loading image file (%s)!", fn); + return false; + } + size = zf.size(); + data = zf.read(); + } break; + #endif + + #ifdef JMA_SUPPORT + case Reader::JMA: { + try { + JMAReader jf(fn); + size = jf.size(); + data = jf.read(); + } catch(JMA::jma_errors jma_error) { + alert("Error loading image file (%s)!", fn); + return false; + } + } break; + #endif + } + + return true; } bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) { @@ -156,7 +160,7 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t if(result == ups::input_crc32_invalid) apply = true; if(result == ups::output_crc32_invalid) apply = true; } - + //if patch application was successful, replace old data, size with new data, size if(apply == true) { delete[] data; @@ -164,16 +168,16 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t memcpy(data, outdata, outsize); } - if(outdata) delete[] outdata; + if(outdata) delete[] outdata; return apply; -} - +} + bool Cartridge::save_file(const char *fn, uint8 *data, uint size) { - FILE *fp = fopen(fn, "wb"); - if(!fp) return false; - fwrite(data, 1, size, fp); - fclose(fp); + file fp; + if(!fp.open(fn, file::mode_write)) return false; + fp.write(data, size); + fp.close(); return true; -} +} #endif //ifdef CART_CPP diff --git a/src/cheat/cheat.cpp b/src/cheat/cheat.cpp index ba1a1a3d..02ad02d1 100644 --- a/src/cheat/cheat.cpp +++ b/src/cheat/cheat.cpp @@ -147,7 +147,7 @@ bool Cheat::read(uint32 addr, uint8 &data) { * update_cheat_status() will scan to see if any codes are * enabled. if any are, make sure the cheat system is on. * otherwise, turn cheat system off to speed up emulation. - *****/ + *****/ void Cheat::update_cheat_status() { for(unsigned i = 0; i < cheat_count; i++) { @@ -295,15 +295,15 @@ bool Cheat::load(const char *fn) { } bool Cheat::save(const char *fn) { - FILE *fp = fopen(fn, "wb"); - if(!fp) return false; + file fp; + if(!fp.open(fn, file::mode_write)) return false; for(unsigned i = 0; i < cheat_count; i++) { - fprintf(fp, "%9s = %8s, \"%s\"\r\n", - index[i].code, - index[i].enabled ? "enabled" : "disabled", - index[i].desc); + fp.print(string() + << index[i].code << " = " + << (index[i].enabled ? "enabled" : "disabled") << ", \"" + << index[i].desc << "\"\r\n"); } - fclose(fp); + fp.close(); return true; } diff --git a/src/cpu/scpu/timing/timing.cpp b/src/cpu/scpu/timing/timing.cpp index 45e91c6d..2a32a6db 100644 --- a/src/cpu/scpu/timing/timing.cpp +++ b/src/cpu/scpu/timing/timing.cpp @@ -150,7 +150,7 @@ void sCPU::cycle_edge() { if(hdma_enabled_channels()) { dma_add_clocks(8 - dma_counter()); //DMA sync status.hdma_mode == 0 ? hdma_init() : hdma_run(); - status.dma_state = DMA_CPUsync; + if(!dma_enabled_channels()) status.dma_state = DMA_CPUsync; } } diff --git a/src/lib/bbase.h b/src/lib/bbase.h index 2976b6c8..4e90f0cd 100644 --- a/src/lib/bbase.h +++ b/src/lib/bbase.h @@ -1,24 +1,24 @@ /* - bbase : version 0.14 ~byuu (2008-04-16) + bbase : version 0.15 ~byuu (2008-09-14) license: public domain */ #ifndef BBASE_H -#define BBASE_H - -#include -typedef int8_t int8; -typedef int16_t int16; -typedef int32_t int32; -typedef int64_t int64; -typedef uint8_t uint8; -typedef uint16_t uint16; -typedef uint32_t uint32; -typedef uint64_t uint64; -typedef unsigned int uint; - -#include -using std::min; +#define BBASE_H + +#include +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef unsigned int uint; + +#include +using std::min; using std::max; #include @@ -41,7 +41,7 @@ using std::max; #endif #if defined(_MSC_VER) -//disable libc deprecation warnings in MSVC 2k5+ + //disable libc deprecation warnings in MSVC 2k5+ #pragma warning(disable:4996) #define NOMINMAX @@ -52,17 +52,10 @@ using std::max; #if defined(_MSC_VER) || defined(__MINGW32__) #define getcwd _getcwd #define ftruncate _chsize - #define mkdir _mkdir #define putenv _putenv #define rmdir _rmdir - #define vsnprintf _vsnprintf + #define vsnprintf _vsnprintf #define usleep(n) Sleep(n / 1000) - - static char *realpath(const char *file_name, char *resolved_name) { - return _fullpath(resolved_name, file_name, PATH_MAX); - } -#else - #define mkdir(path) (mkdir)(path, 0755); #endif /***** @@ -87,22 +80,29 @@ using std::max; * OS localization *****/ -//userpath(output) retrieves path to user's home folder -//output must be at least as large as PATH_MAX - #if defined(_MSC_VER) || defined(__MINGW32__) -static char *userpath(char *output) { - strcpy(output, "."); //failsafe - SHGetFolderPath(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, output); +static char* realpath(const char *file_name, char *resolved_name) { + wchar_t filename[PATH_MAX] = L""; + _wfullpath(filename, utf16(file_name), PATH_MAX); + strcpy(resolved_name, utf8(filename)); + return resolved_name; +} + +static char* userpath(char *output) { + wchar_t path[PATH_MAX] = L"."; //failsafe + SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, path); + strcpy(output, utf8(path)); return output; } +#define mkdir(path) _wmkdir(utf16(path)) #else -static char *userpath(char *output) { +static char* userpath(char *output) { strcpy(output, "."); //failsafe struct passwd *userinfo = getpwuid(getuid()); if(userinfo) { strcpy(output, userinfo->pw_dir); } return output; } +#define mkdir(path) (mkdir)(path, 0755); #endif template inline T minmax(const T x) { @@ -113,93 +113,48 @@ template inline T minmax(const T x) { * endian wrappers *****/ -#ifndef ARCH_MSB -//little-endian: uint8[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 - #define order_lsb2(a,b) a,b - #define order_lsb3(a,b,c) a,b,c - #define order_lsb4(a,b,c,d) a,b,c,d - #define order_lsb5(a,b,c,d,e) a,b,c,d,e - #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f - #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g - #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h - #define order_msb2(a,b) b,a - #define order_msb3(a,b,c) c,b,a - #define order_msb4(a,b,c,d) d,c,b,a - #define order_msb5(a,b,c,d,e) e,d,c,b,a - #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a - #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a - #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a -#else -//big-endian: uint8[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 - #define order_lsb2(a,b) b,a - #define order_lsb3(a,b,c) c,b,a - #define order_lsb4(a,b,c,d) d,c,b,a - #define order_lsb5(a,b,c,d,e) e,d,c,b,a - #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a - #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a - #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a - #define order_msb2(a,b) a,b - #define order_msb3(a,b,c) a,b,c - #define order_msb4(a,b,c,d) a,b,c,d - #define order_msb5(a,b,c,d,e) a,b,c,d,e - #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f - #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g - #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#ifndef ARCH_MSB +//little-endian: uint8[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else +//big-endian: uint8[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h #endif /***** * libc extensions *****/ - + //pseudo-random number generator static unsigned prng() { static unsigned n = 0; return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); } -static uint64 fget(FILE *fp, unsigned length = 1) { - uint64 data = 0; - for(unsigned i = 0; i < length; i++) { - data |= fgetc(fp) << (i << 3); - } - return data; -} - -static void fput(FILE *fp, uint64 data, unsigned length = 1) { - for(unsigned i = 0; i < length; i++) { - fputc(data >> (i << 3), fp); - } -} - -static bool fexists(const char *fn) { - FILE *fp = fopen(fn, "rb"); - if(!fp) return false; - fclose(fp); - fp = 0; - return true; -} - -static unsigned fsize(FILE *fp) { - if(!fp) return 0; - unsigned pos = ftell(fp); - fseek(fp, 0, SEEK_END); - unsigned size = ftell(fp); - fseek(fp, pos, SEEK_SET); - return size; -} - -static unsigned fsize(const char *fn) { - FILE *fp = fopen(fn, "rb"); - if(!fp) return 0; - fseek(fp, 0, SEEK_END); - unsigned size = ftell(fp); - fclose(fp); - fp = 0; - return size; -} - -static int fresize(FILE *fp, long size) { - return ftruncate(fileno(fp), size); -} - #endif //ifndef BBASE_H diff --git a/src/lib/hiro/cc.bat b/src/lib/hiro/cc.bat deleted file mode 100644 index 5947ec02..00000000 --- a/src/lib/hiro/cc.bat +++ /dev/null @@ -1,6 +0,0 @@ -mingw32-g++ -c test/test.cpp -I. -I../ -mingw32-g++ -c hiro.cpp -I. -I../ -mingw32-g++ -c ../nall/string.cpp -I. -I../ -mingw32-g++ test.o hiro.o string.o -o test_app.exe -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32 -@pause -@del *.o diff --git a/src/lib/hiro/cc.sh b/src/lib/hiro/cc.sh deleted file mode 100644 index 11d88958..00000000 --- a/src/lib/hiro/cc.sh +++ /dev/null @@ -1,6 +0,0 @@ -clear -g++ -c test/test.cpp -I. -I../ -g++ -c hiro.cpp `pkg-config --cflags gtk+-2.0` -I. -I../ -g++ -c ../nall/string.cpp -I. -I../ -g++ test.o hiro.o string.o -o test_app `pkg-config --libs gtk+-2.0` -lXtst -rm *.o diff --git a/src/lib/hiro/win/hiro.cpp b/src/lib/hiro/win/hiro.cpp index 22e4dec8..fb05ab5c 100644 --- a/src/lib/hiro/win/hiro.cpp +++ b/src/lib/hiro/win/hiro.cpp @@ -4,11 +4,14 @@ using nall::min; using nall::max; +#include +using nall::utf8; +using nall::utf16; + namespace libhiro { LRESULT CALLBACK phiro_wndproc(HWND, UINT, WPARAM, LPARAM); -#include "utf.cpp" #include "keymap.cpp" #include "widget.cpp" #include "window.cpp" diff --git a/src/lib/nall/config.hpp b/src/lib/nall/config.hpp index cb17b161..686336e9 100644 --- a/src/lib/nall/config.hpp +++ b/src/lib/nall/config.hpp @@ -2,6 +2,7 @@ #define NALL_CONFIG_HPP #include +#include #include #include @@ -13,7 +14,7 @@ class configuration { public: array list; - bool load(const char *fn) const; + bool load(const char *fn); bool save(const char *fn) const; void add(setting *setting_) { list.add(setting_); } }; @@ -116,28 +117,17 @@ private: } }; -inline bool configuration::load(const char *fn) const { - FILE *fp = fopen(fn, "rb"); - if(!fp) return false; - - string data; - lstring line, part, subpart; - +inline bool configuration::load(const char *fn) { //load the config file into memory - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - fseek(fp, 0, SEEK_SET); - char *buffer = (char*)malloc(size + 1); - fread(buffer, 1, size, fp); - fclose(fp); - buffer[size] = 0; - strcpy(data, buffer); - free(buffer); + string data; + if(!fread(data, fn)) return false; //split the file into lines replace(data, "\r\n", "\n"); qreplace(data, "\t", ""); qreplace(data, " ", ""); + + lstring line, part, subpart; split(line, "\n", data); for(unsigned i = 0; i < count(line); i++) { @@ -157,8 +147,8 @@ inline bool configuration::load(const char *fn) const { } inline bool configuration::save(const char *fn) const { - FILE *fp = fopen(fn, "wb"); - if(!fp) return false; + file fp; + if(!fp.open(fn, file::mode_write)) return false; for(unsigned i = 0; i < list.size(); i++) { string data; @@ -166,18 +156,20 @@ inline bool configuration::save(const char *fn) const { strcpy(data, list[i]->description); replace(data, "\r\n", "\n"); split(line, "\n", data); + + string temp; for(unsigned l = 0; l < count(line); l++) { - if(line[l] != "") fprintf(fp, "# %s\r\n", (const char*)line[l]); + if(line[l] != "") fp.print(string() << "# " << line[l] << "\r\n"); } string default_, value_; list[i]->get_default(default_); - fprintf(fp, "# (default = %s)\r\n", (const char*)default_); + fp.print(string() << "# (default = " << default_ << ")\r\n"); list[i]->get(value_); - fprintf(fp, "%s = %s\r\n\r\n", list[i]->name, (const char*)value_); + fp.print(string() << list[i]->name << " = " << value_ << "\r\n\r\n"); } - fclose(fp); + fp.close(); return true; } diff --git a/src/lib/nall/file.hpp b/src/lib/nall/file.hpp index 3fe59550..3dabc2d8 100644 --- a/src/lib/nall/file.hpp +++ b/src/lib/nall/file.hpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace nall { @@ -69,6 +70,11 @@ public: while(length--) write(*buffer++); } + void print(const char *string) { + if(!string) return; + while(*string) write(*string++); + } + void flush() { buffer_flush(); fflush(fp); @@ -112,13 +118,38 @@ public: return file_offset >= file_size; } + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + bool open() { + return fp; + } + bool open(const char *fn, FileMode mode) { if(fp) return false; + switch(file_mode = mode) { + #if !defined(_WIN32) case mode_read: fp = fopen(fn, "rb"); break; case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering case mode_readwrite: fp = fopen(fn, "rb+"); break; case mode_writeread: fp = fopen(fn, "wb+"); break; + #else + case mode_read: fp = _wfopen(utf16(fn), L"rb"); break; + case mode_write: fp = _wfopen(utf16(fn), L"wb+"); break; + case mode_readwrite: fp = _wfopen(utf16(fn), L"rb+"); break; + case mode_writeread: fp = _wfopen(utf16(fn), L"wb+"); break; + #endif } if(!fp) return false; buffer_offset = -1; //invalidate buffer diff --git a/src/lib/nall/filemap.hpp b/src/lib/nall/filemap.hpp index 72289a95..bc73c49f 100644 --- a/src/lib/nall/filemap.hpp +++ b/src/lib/nall/filemap.hpp @@ -2,6 +2,7 @@ #define NALL_FILEMAP_HPP #include +#include #include #include @@ -72,7 +73,7 @@ private: break; } - p_filehandle = CreateFile(filename, desired_access, FILE_SHARE_READ, NULL, + p_filehandle = CreateFileW(utf16(filename), desired_access, FILE_SHARE_READ, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); if(p_filehandle == INVALID_HANDLE_VALUE) return false; diff --git a/src/lib/nall/serial.hpp b/src/lib/nall/serial.hpp new file mode 100644 index 00000000..706ebfb5 --- /dev/null +++ b/src/lib/nall/serial.hpp @@ -0,0 +1,82 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + +class serial { +public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB); + attr.c_cflag |= (CS8 | CREAD | CLOCAL); + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + +private: + int port; + bool port_open; + termios original_attr; +}; + +} //namespace nall + +#endif //ifndef NALL_SERIAL_HPP diff --git a/src/lib/nall/sort.hpp b/src/lib/nall/sort.hpp index 09f5f214..08106b76 100644 --- a/src/lib/nall/sort.hpp +++ b/src/lib/nall/sort.hpp @@ -4,15 +4,15 @@ namespace nall { template inline void swap(T &x, T &y) { -T z = x; + T temp = x; x = y; - y = z; + y = temp; } template void sort(T list[], unsigned length) { for(unsigned d = 0; d < length; d++) { - unsigned min = d; + unsigned min = d; for(unsigned s = d + 1; s < length; s++) { if(list[s] < list[min]) { min = s; } } @@ -22,10 +22,10 @@ void sort(T list[], unsigned length) { } } -template -void sort(T list[], unsigned length, Tcmp comparator) { +template +void sort(T list[], unsigned length, Comparator comparator) { for(unsigned d = 0; d < length; d++) { - unsigned min = d; + unsigned min = d; for(unsigned s = d + 1; s < length; s++) { if(comparator(list[s], list[min]) == true) { min = s; } } diff --git a/src/lib/nall/string.cpp b/src/lib/nall/string.cpp index 508371e1..f1818bd4 100644 --- a/src/lib/nall/string.cpp +++ b/src/lib/nall/string.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/src/lib/nall/string/class.cpp b/src/lib/nall/string/class.cpp index 8c167c9c..9b85f14c 100644 --- a/src/lib/nall/string/class.cpp +++ b/src/lib/nall/string/class.cpp @@ -63,7 +63,7 @@ string& string::operator=(const string &str) { } string& string::operator<<(int num) { -string temp(num); + string temp(num); strcat(*this, temp); return *this; } @@ -147,7 +147,7 @@ size_t strlcat(nall::string &dest, const char *src, size_t length) { } nall::string substr(const char *src, size_t start, size_t length) { -nall::string dest; + nall::string dest; if(length == 0) { //copy entire string strcpy(dest, src + start); } else { //copy partial string @@ -171,28 +171,28 @@ nall::string& trim_once (nall::string &str, const char *key) { trim_once (str(), /* arithmetic <> string */ nall::string strhex(uintmax_t value) { -nall::string temp; + nall::string temp; temp.reserve(strhex(0, value)); strhex(temp(), value); return temp; } nall::string strdec(intmax_t value) { -nall::string temp; + nall::string temp; temp.reserve(strdec(0, value)); strdec(temp(), value); return temp; } nall::string strbin(uintmax_t value) { -nall::string temp; + nall::string temp; temp.reserve(strbin(0, value)); strbin(temp(), value); return temp; } nall::string strdouble(double value) { -nall::string temp; + nall::string temp; temp.reserve(strdouble(0, value)); strdouble(temp(), value); return temp; @@ -203,13 +203,17 @@ nall::string temp; bool fread(nall::string &str, const char *filename) { strcpy(str, ""); -FILE *fp = fopen(filename, "rb"); + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(nall::utf16(filename), L"rb"); + #endif if(!fp)return false; fseek(fp, 0, SEEK_END); -size_t size = ftell(fp); + size_t size = ftell(fp); rewind(fp); -char *fdata = (char*)malloc(size + 1); + char *fdata = (char*)malloc(size + 1); fread(fdata, 1, size, fp); fclose(fp); fdata[size] = 0; diff --git a/src/lib/nall/ups.hpp b/src/lib/nall/ups.hpp index 75c2e7a0..737e8527 100644 --- a/src/lib/nall/ups.hpp +++ b/src/lib/nall/ups.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -25,8 +26,7 @@ public: }; ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { - fp = fopen(patch_fn, "wb"); - if(!fp) return patch_unwritable; + if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; crc32 = ~0; uint32_t x_crc32 = crc32_calculate(x_data, x_size); @@ -77,7 +77,7 @@ public: uint32_t p_crc32 = ~crc32; for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); - fclose(fp); + fp.close(); return ok; } @@ -147,7 +147,7 @@ public: } private: - FILE *fp; + file fp; uint32_t crc32; const uint8_t *p_buffer; @@ -158,7 +158,7 @@ private: } void write(uint8_t n) { - fputc(n, fp); + fp.write(n); crc32 = crc32_adjust(crc32, n); } diff --git a/src/lib/hiro/win/utf.cpp b/src/lib/nall/utf8.hpp similarity index 73% rename from src/lib/hiro/win/utf.cpp rename to src/lib/nall/utf8.hpp index aa1386ca..aedcbfdd 100644 --- a/src/lib/hiro/win/utf.cpp +++ b/src/lib/nall/utf8.hpp @@ -1,6 +1,18 @@ -/***** - * UTF-8 to UTF-16 - *****/ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +#include + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#include + +namespace nall { + +//UTF-8 to UTF-16 class utf16 { public: operator wchar_t*() { @@ -26,9 +38,7 @@ private: wchar_t *buffer; }; -/***** - * UTF-16 to UTF-8 - *****/ +//UTF-16 to UTF-8 class utf8 { public: operator char*() { @@ -53,3 +63,9 @@ public: private: char *buffer; }; + +} //namespace nall + +#endif //if defined(_WIN32) + +#endif //ifndef NALL_UTF8_HPP diff --git a/src/reader/filereader.cpp b/src/reader/filereader.cpp index a1342db8..0b26529c 100644 --- a/src/reader/filereader.cpp +++ b/src/reader/filereader.cpp @@ -3,7 +3,7 @@ #include "filereader.h" unsigned FileReader::size() { - return filesize; + return fp.size(); } //This function will allocate memory even if open() fails. @@ -15,44 +15,35 @@ uint8_t* FileReader::read(unsigned length) { if(length == 0) { //read the entire file into RAM - data = new(zeromemory) uint8_t[filesize]; - if(fp) fread(data, 1, filesize, fp); - } else if(length > filesize) { + data = new(zeromemory) uint8_t[fp.size()]; + if(fp.open()) fp.read(data, fp.size()); + } else if(length > fp.size()) { //read the entire file into RAM, pad the rest with 0x00s data = new(zeromemory) uint8_t[length]; - if(fp) fread(data, 1, filesize, fp); + if(fp.open()) fp.read(data, fp.size()); } else { //filesize >= length //read only what was requested data = new(zeromemory) uint8_t[length]; - if(fp) fread(data, 1, length, fp); + if(fp.open()) fp.read(data, length); } return data; } bool FileReader::ready() { - return (fp != 0); + return fp.open(); } FileReader::FileReader(const char *fn) { - fp = fopen(fn, "rb"); - if(!fp) return; - - fseek(fp, 0, SEEK_END); - filesize = ftell(fp); - rewind(fp); + if(!fp.open(fn, file::mode_read)) return; //empty file? - if(filesize == 0) { - fclose(fp); - fp = 0; + if(fp.size() == 0) { + fp.close(); } } FileReader::~FileReader() { - if(fp) { - fclose(fp); - fp = 0; - } + if(fp.open()) fp.close(); } #endif //ifdef READER_CPP diff --git a/src/reader/filereader.h b/src/reader/filereader.h index c8f6df43..c48819c1 100644 --- a/src/reader/filereader.h +++ b/src/reader/filereader.h @@ -8,6 +8,5 @@ public: ~FileReader(); private: - FILE *fp; - unsigned filesize; + file fp; }; diff --git a/src/reader/reader.cpp b/src/reader/reader.cpp index 8bd8b44a..2fb18f4c 100644 --- a/src/reader/reader.cpp +++ b/src/reader/reader.cpp @@ -13,13 +13,13 @@ #endif Reader::Type Reader::detect(const char *fn, bool inspectheader) { - FILE *fp = fopen(fn, "rb"); - if(!fp) return Unknown; + file fp; + if(!fp.open(fn, file::mode_read)) return Unknown; uint8_t p[8]; memset(p, 0, sizeof p); - fread(p, 1, 8, fp); - fclose(fp); + fp.read(p, 8); + fp.close(); if(inspectheader == true) { //inspect file header to determine type diff --git a/src/reader/zipreader.cpp b/src/reader/zipreader.cpp index ca07ade1..12ae9f33 100644 --- a/src/reader/zipreader.cpp +++ b/src/reader/zipreader.cpp @@ -1,52 +1,57 @@ #ifdef READER_CPP -#include "zipreader.h" - -unsigned ZipReader::size() { - return filesize; -} - -#define MAXROM 0x800000 - -uint8_t* ZipReader::read(unsigned length) { - uint8_t *data = 0; +#include "zipreader.h" - if(!filesize) return 0; +unsigned ZipReader::size() { + return filesize; +} - if(length <= filesize) { - //read the entire file into RAM - data = new(zeromemory) uint8_t[filesize]; - unzReadCurrentFile(zipfile, data, filesize); - } else if(length > filesize) { - //read the entire file into RAM, pad the rest with 0x00s - data = new(zeromemory) uint8_t[length]; - unzReadCurrentFile(zipfile, data, filesize); +#define MAXROM 0x800000 + +uint8_t* ZipReader::read(unsigned length) { + uint8_t *data = 0; + + if(!filesize) return 0; + + if(length <= filesize) { + //read the entire file into RAM + data = new(zeromemory) uint8_t[filesize]; + unzReadCurrentFile(zipfile, data, filesize); + } else if(length > filesize) { + //read the entire file into RAM, pad the rest with 0x00s + data = new(zeromemory) uint8_t[length]; + unzReadCurrentFile(zipfile, data, filesize); } - - return data; -} - -ZipReader::ZipReader(const char *fn) : filesize(0) { - unz_file_info cFileInfo; //Create variable to hold info for a compressed file - char cFileName[sizeof(cname)]; - - if(zipfile = unzOpen(fn)) { //Open zip file - for(int cFile = unzGoToFirstFile(zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile(zipfile)) { - //Gets info on current file, and places it in cFileInfo - unzGetCurrentFileInfo(zipfile, &cFileInfo, cFileName, sizeof(cname), 0, 0, 0, 0); - - if((cFileInfo.uncompressed_size <= MAXROM+512) && (cFileInfo.uncompressed_size > filesize)) { - strcpy(cname, cFileName); - filesize = cFileInfo.uncompressed_size; - } - } - - if(filesize) { - unzLocateFile(zipfile, cname, 1); - unzOpenCurrentFile(zipfile); - } - } -} + + return data; +} + +bool ZipReader::ready() { + return zipready; +} + +ZipReader::ZipReader(const char *fn) : filesize(0), zipready(false) { + unz_file_info cFileInfo; //Create variable to hold info for a compressed file + char cFileName[sizeof(cname)]; + + if(zipfile = unzOpen(fn)) { //Open zip file + for(int cFile = unzGoToFirstFile(zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile(zipfile)) { + //Gets info on current file, and places it in cFileInfo + unzGetCurrentFileInfo(zipfile, &cFileInfo, cFileName, sizeof(cname), 0, 0, 0, 0); + + if((cFileInfo.uncompressed_size <= MAXROM+512) && (cFileInfo.uncompressed_size > filesize)) { + strcpy(cname, cFileName); + filesize = cFileInfo.uncompressed_size; + } + } + + if(filesize) { + unzLocateFile(zipfile, cname, 1); + unzOpenCurrentFile(zipfile); + zipready = true; + } + } +} ZipReader::~ZipReader() { if(zipfile) { diff --git a/src/reader/zipreader.h b/src/reader/zipreader.h index 0db3bdf7..91cce200 100644 --- a/src/reader/zipreader.h +++ b/src/reader/zipreader.h @@ -7,12 +7,14 @@ class ZipReader : public Reader { public: unsigned size(); uint8_t* read(unsigned length = 0); + bool ready(); ZipReader(const char *fn); - ~ZipReader(); - -private: - unzFile zipfile; - uint32 filesize; - char cname[4096]; + ~ZipReader(); + +private: + unzFile zipfile; + uint32 filesize; + bool zipready; + char cname[4096]; }; diff --git a/src/snes/audio/audio.cpp b/src/snes/audio/audio.cpp index 30fa1204..aa643e93 100644 --- a/src/snes/audio/audio.cpp +++ b/src/snes/audio/audio.cpp @@ -4,8 +4,10 @@ void SNES::Audio::update(uint16 l_sample, uint16 r_sample) { if(pcmfp) { - fput(pcmfp, l_sample, 2); - fput(pcmfp, r_sample, 2); + fputc(l_sample >> 0, pcmfp); + fputc(l_sample >> 8, pcmfp); + fputc(r_sample >> 0, pcmfp); + fputc(r_sample >> 8, pcmfp); } snesinterface.audio_sample(l_sample, r_sample); diff --git a/src/ui/config.cpp b/src/ui/config.cpp index 4ce92695..22e9cdb7 100644 --- a/src/ui/config.cpp +++ b/src/ui/config.cpp @@ -109,7 +109,7 @@ struct Audio { static integral_setting synchronize; } audio; integral_setting Audio::output_frequency(config(), "audio.output_frequency", "Sound card audio output frequency", integral_setting::decimal, 48000); -integral_setting Audio::input_frequency(config(), "audio.input_frequency", "Emulator audio input frequency", integral_setting::decimal, 31960); +integral_setting Audio::input_frequency(config(), "audio.input_frequency", "Emulator audio input frequency", integral_setting::decimal, 32000); integral_setting Audio::latency(config(), "audio.latency", "Sound card latency (in ms)", integral_setting::decimal, 100); integral_setting Audio::volume(config(), "audio.volume", "Audio volume (10 - 100)", integral_setting::decimal, 100); integral_setting Audio::mute(config(), "audio.mute", "Mute audio playback", integral_setting::boolean, false); diff --git a/src/ui/event.cpp b/src/ui/event.cpp index 546fecde..5ab186b1 100644 --- a/src/ui/event.cpp +++ b/src/ui/event.cpp @@ -302,6 +302,7 @@ bool load_rom(char *fn) { ); } +//File -> Load ROM action void load_rom() { char fn[PATH_MAX]; if(load_rom(fn) == false) return; @@ -311,8 +312,9 @@ void load_rom() { void load_cart_normal(const char *filename) { if(!filename || !*filename) return; - if(cartridge.loaded() == true) cartridge.unload(); + unload_rom(); cartridge.load_cart_normal(filename); + if(cartridge.loaded() == false) return; app.pause = false; snes.power(); @@ -338,8 +340,9 @@ void load_cart_normal(const char *filename) { void load_cart_bsx(const char *base, const char *slot) { if(!base || !*base) return; - if(cartridge.loaded() == true) cartridge.unload(); + unload_rom(); cartridge.load_cart_bsx(base, slot); + if(cartridge.loaded() == false) return; app.pause = false; snes.power(); @@ -357,8 +360,9 @@ void load_cart_bsx(const char *base, const char *slot) { void load_cart_bsc(const char *base, const char *slot) { if(!base || !*base) return; - if(cartridge.loaded() == true) cartridge.unload(); + unload_rom(); cartridge.load_cart_bsc(base, slot); + if(cartridge.loaded() == false) return; app.pause = false; snes.power(); @@ -376,8 +380,9 @@ void load_cart_bsc(const char *base, const char *slot) { void load_cart_st(const char *base, const char *slotA, const char *slotB) { if(!base || !*base) return; - if(cartridge.loaded() == true) cartridge.unload(); + unload_rom(); cartridge.load_cart_st(base, slotA, slotB); + if(cartridge.loaded() == false) return; app.pause = false; snes.power(); @@ -393,11 +398,12 @@ void load_cart_st(const char *base, const char *slotA, const char *slotB) { } void unload_rom() { - if(cartridge.loaded() == true) { - cartridge.unload(); - video.clear(); - audio.clear(); - } + if(cartridge.loaded() == false) return; + + cartridge.unload(); + video.clear(); + audio.clear(); + window_main.menu_file_unload.disable(); window_main.menu_file_reset.disable(); window_main.menu_file_power.disable(); diff --git a/src/ui/event.h b/src/ui/event.h index dd492b59..c37e3b57 100644 --- a/src/ui/event.h +++ b/src/ui/event.h @@ -1,47 +1,47 @@ namespace event { void keydown(uint16_t); -void keyup(uint16_t); - -struct VideoSettings { - uint mode; - bool synchronize; - bool aspect_correction; - uint region; - uint multiplier; - uint hardware_filter; - uint software_filter; -} video_settings; -void load_video_settings(); - -void update_aspect_correction(bool); -void update_multiplier(uint); -void update_region(uint); -void update_hardware_filter(uint); -void update_software_filter(uint); - -void update_frameskip(int); -void update_emulation_speed(int); - -void update_controller_port1(int); -void update_controller_port2(int); - +void keyup(uint16_t); + +struct VideoSettings { + uint mode; + bool synchronize; + bool aspect_correction; + uint region; + uint multiplier; + uint hardware_filter; + uint software_filter; +} video_settings; +void load_video_settings(); + +void update_aspect_correction(bool); +void update_multiplier(uint); +void update_region(uint); +void update_hardware_filter(uint); +void update_software_filter(uint); + +void update_frameskip(int); +void update_emulation_speed(int); + +void update_controller_port1(int); +void update_controller_port2(int); + void update_video_settings(); void update_opacity(); -void toggle_fullscreen(); +void toggle_fullscreen(); void toggle_menubar(); -void toggle_statusbar(); - -bool load_rom(char*); -void load_rom(); -void load_cart_normal(const char*); -void load_cart_bsx(const char*, const char*); -void load_cart_bsc(const char*, const char*); -void load_cart_st(const char*, const char*, const char*); -void unload_rom(); -void reset(); +void toggle_statusbar(); + +bool load_rom(char*); +void load_rom(); +void load_cart_normal(const char*); +void load_cart_bsx(const char*, const char*); +void load_cart_bsc(const char*, const char*); +void load_cart_st(const char*, const char*, const char*); +void unload_rom(); +void reset(); void power(); -void quit(); - +void quit(); + } //namespace event diff --git a/src/ui/main.cpp b/src/ui/main.cpp index efeff810..e59f2036 100644 --- a/src/ui/main.cpp +++ b/src/ui/main.cpp @@ -55,6 +55,7 @@ void dprintf(const char *s, ...) { va_start(args, s); vsprintf(str, s, args); va_end(args); + fprintf(stdout, "%s\r\n", str); } @@ -89,7 +90,7 @@ void set_config_filenames() { //locate bsnes.cfg strcpy(filename, config::path.base); strcat(filename, "bsnes.cfg"); - if(!fexists(filename)) { + if(!file::exists(filename)) { strcpy(filename, config::path.user); strcat(filename, ".bsnes"); mkdir(filename); @@ -100,7 +101,7 @@ void set_config_filenames() { //locate locale.cfg strcpy(filename, config::path.base); strcat(filename, "locale.cfg"); - if(!fexists(filename)) { + if(!file::exists(filename)) { strcpy(filename, config::path.user); strcat(filename, ".bsnes"); mkdir(filename); @@ -136,18 +137,26 @@ void run() { #if defined(PLATFORM_WIN) int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { -int argc = __argc; -char **argv = __argv; + //On Windows, argv[] is in 7-bit ANSI format, UTF-8 chars are converted to '?'s. + //Need argv[] to be in UTF-8 format to properly determine realpath() and default image filepaths. + //To do this, parse command line in UTF-16, and then convert to UTF-8. + int argc; + wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + char **argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new(zeromemory) char[PATH_MAX]; + strcpy(argv[i], utf8(wargv[i])); + } #else int main(int argc, char *argv[]) { -#endif -/* +#endif /* + int main(int argc, char *argv[]) { */ get_paths(argv[0]); set_config_filenames(); config::config().load(config::bsnes_cfg); - if(fexists(config::bsnes_cfg) == false) { + if(file::exists(config::bsnes_cfg) == false) { //in case program crashes on first run, save config file //settings, so that they can be modified by hand ... config::config().save(config::bsnes_cfg); @@ -159,12 +168,14 @@ int main(int argc, char *argv[]) { */ if(app.term == true) goto app_term; snes.init(); - if(argc >= 2 && fexists(argv[1])) { + if(argc >= 2 && file::exists(argv[1])) { cartridge.load_cart_normal(argv[1]); - snes.power(); - window_main.menu_file_unload.enable(); - window_main.menu_file_reset.enable(); - window_main.menu_file_power.enable(); + if(cartridge.loaded()) { + snes.power(); + window_main.menu_file_unload.enable(); + window_main.menu_file_reset.enable(); + window_main.menu_file_power.enable(); + } } while(app.term == false) run();