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:
byuu 2008-09-15 05:29:14 +00:00
parent 8c591ce44a
commit 0114e10ede
29 changed files with 546 additions and 436 deletions

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -89,7 +89,7 @@ char* Cartridge::get_cheat_filename(const char *source, const char *extension) {
}
bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression) {
if(fexists(fn) == false) return false;
if(file::exists(fn) == false) return false;
Reader::Type filetype = Reader::Normal;
if(compression == CompressionInspect) filetype = Reader::detect(fn, true);
@ -122,6 +122,10 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionM
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;
@ -169,10 +173,10 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t
}
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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -1,5 +1,5 @@
/*
bbase : version 0.14 ~byuu (2008-04-16)
bbase : version 0.15 ~byuu (2008-09-14)
license: public domain
*/
@ -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 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,15 +80,21 @@ 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* 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) {
strcpy(output, "."); //failsafe
SHGetFolderPath(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, 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) {
strcpy(output, "."); //failsafe
@ -103,6 +102,7 @@ static char *userpath(char *output) {
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) {
@ -157,49 +157,4 @@ static unsigned prng() {
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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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;
}

View File

@ -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

View File

@ -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;

82
src/lib/nall/serial.hpp Normal file
View File

@ -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

View File

@ -4,9 +4,9 @@
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>
@ -22,8 +22,8 @@ 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;
for(unsigned s = d + 1; s < length; s++) {

View File

@ -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>

View File

@ -203,7 +203,11 @@ nall::string temp;
bool fread(nall::string &str, const char *filename) {
strcpy(str, "");
#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);

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -8,6 +8,5 @@ public:
~FileReader();
private:
FILE *fp;
unsigned filesize;
file fp;
};

View File

@ -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

View File

@ -26,7 +26,11 @@ uint8_t* ZipReader::read(unsigned length) {
return data;
}
ZipReader::ZipReader(const char *fn) : filesize(0) {
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)];
@ -44,6 +48,7 @@ ZipReader::ZipReader(const char *fn) : filesize(0) {
if(filesize) {
unzLocateFile(zipfile, cname, 1);
unzOpenCurrentFile(zipfile);
zipready = true;
}
}
}

View File

@ -7,6 +7,7 @@ class ZipReader : public Reader {
public:
unsigned size();
uint8_t* read(unsigned length = 0);
bool ready();
ZipReader(const char *fn);
~ZipReader();
@ -14,5 +15,6 @@ public:
private:
unzFile zipfile;
uint32 filesize;
bool zipready;
char cname[4096];
};

View File

@ -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);

View File

@ -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);

View File

@ -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) {
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();

View File

@ -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,13 +168,15 @@ 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]);
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();