mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v036 release.
This release fixes a somewhat serious bug introduced in v035, and also vastly improves Windows support for non-ANSI filenames. The bug was triggered when HDMA would occur during DMA. If the DMA were long enough, subsequent HDMA transfers would be blocked. This caused graphical glitches in Star Ocean, Super Mario Kart, and possible more games. If you noticed any regressions from v034 to v035, this was almost certainly the cause. Once again, we're operating under the assumption that there are no known bugs currently, so please let us know here if you find any. I've also rewritten the file handling for the emulator. On Windows, attempting to load a file with non-ANSI characters (eg Russian, Japanese, etc) would cause these characters to be removed. This meant that no version of bsnes thus far could load these files. This problem was exacerbated when I ported the user interface to Unicode (UTF-16), this caused even config and locale file loading to crash the emulator. The root of the problem is that Windows only accepts non-ANSI strings in UTF-16 format, whereas bsnes' UI wrapper converts strings to UTF-8 interally. When passing these file names to the standard file functions (fopen(), std::ifstream, etc), file loading would fail. To fix this, I replaced all file access functions with a new version that would convert the UTF-8 filenames back to UTF-16, and use appropriate access functions (_wfopen(), _wmkdir(), etc.) ... but there is still one limitation to this: ZIP and GZ support use zlib, and JMA support uses libjma. Neither of these libraries convert UTF-8 strings to UTF-16 before attempting to open files. Due to licensing issues, as well as technical issues, I am unable to correct this at this time. What this means is that loading ZIP, GZ and JMA files; on Windows only; and with Unicode characters in the file name only; will cause the image load to fail. Loading uncompressed images (SMC, SFC, etc) will work with or without Unicode on all platforms. I tried to be as thorough as possible with this fix: command-line arguments (via CommandLineToArvW + GetCommandLineW), user path (via SHGetFolderPathW), real path (via _wfullpath),folder creation (via _wmkdir) and file access/existence checks (via _wfopen) were updated in all cases. I also updated file loading for ROMs (SMC, SFC, etc), save RAM (SRM), real-time clock save (RTC), cheat files (CHT), UPS patches (UPS) and both configuration files (bsnes.cfg and locale.cfg.) Configuration file loading should work even if your username contains non-ANSI characters, and it should also detect config files put in the same folder as the bsnes executable, even if the path to the executable contains non-ANSI characters. Still, if you spot any bugs, aside from the ZIP/GZ/JMA loading issue, please let me know via e-mail at setsunakun0; at hotmail. Lastly, I'd like to apologize for the poor support for non-ANSI filenames in the past. Using an English version of Windows didn't expose the problems to me. I'll be more thorough in the future with this.
This commit is contained in:
parent
8c591ce44a
commit
0114e10ede
24
readme.txt
24
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
|
||||
|
|
|
@ -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 <nall/bit.hpp>
|
||||
#include <nall/config.hpp>
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/modulo.hpp>
|
||||
#include <nall/new.hpp>
|
||||
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
175
src/lib/bbase.h
175
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 <nall/stdint.hpp>
|
||||
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 <algorithm>
|
||||
using std::min;
|
||||
#define BBASE_H
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
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 <algorithm>
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
#include <assert.h>
|
||||
|
@ -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<int min, int max, typename T> inline T minmax(const T x) {
|
||||
|
@ -113,93 +113,48 @@ template<int min, int max, typename T> 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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -4,11 +4,14 @@
|
|||
using nall::min;
|
||||
using nall::max;
|
||||
|
||||
#include <nall/utf8.hpp>
|
||||
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"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define NALL_CONFIG_HPP
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
|
@ -13,7 +14,7 @@ class configuration {
|
|||
public:
|
||||
array<setting*> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
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
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define NALL_FILEMAP_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
#ifndef NALL_SERIAL_HPP
|
||||
#define NALL_SERIAL_HPP
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
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
|
|
@ -4,15 +4,15 @@
|
|||
namespace nall {
|
||||
|
||||
template<typename T> inline void swap(T &x, T &y) {
|
||||
T z = x;
|
||||
T temp = x;
|
||||
x = y;
|
||||
y = z;
|
||||
y = temp;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<typename T, typename Tcmp>
|
||||
void sort(T list[], unsigned length, Tcmp comparator) {
|
||||
template<typename T, typename Comparator>
|
||||
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; }
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <math.h>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/static.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
|
||||
#include <nall/string.hpp>
|
||||
#include <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;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
/*****
|
||||
* UTF-8 to UTF-16
|
||||
*****/
|
||||
#ifndef NALL_UTF8_HPP
|
||||
#define NALL_UTF8_HPP
|
||||
|
||||
#include <nall/new.hpp>
|
||||
|
||||
//UTF-8 <> UTF-16 conversion
|
||||
//used only for Win32; Linux, etc use UTF-8 internally
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
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
|
|
@ -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
|
||||
|
|
|
@ -8,6 +8,5 @@ public:
|
|||
~FileReader();
|
||||
|
||||
private:
|
||||
FILE *fp;
|
||||
unsigned filesize;
|
||||
file fp;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue