Merge pull request #35 from negativeExponent/update

Sync to upstream 1.24.0 and add misc features
This commit is contained in:
hizzlekizzle 2019-12-25 08:55:02 -06:00 committed by GitHub
commit 46ac62b4c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 2706 additions and 1283 deletions

3
.gitignore vendored
View File

@ -3,4 +3,5 @@
*.dll
*.dylib
/old
.vscode
.clang-format

View File

@ -1,5 +1,6 @@
DEBUG = 0
FRONTEND_SUPPORTS_RGB565 = 1
HAVE_NO_LANGEXTRA = 0
CORE_DIR := .
@ -583,7 +584,7 @@ LDFLAGS += $(fpic) $(SHARED)
FLAGS += $(fpic) $(NEW_GCC_FLAGS)
FLAGS += $(INCFLAGS) $(INCFLAGS_PLATFORM)
FLAGS += $(ENDIANNESS_DEFINES) -DSIZEOF_DOUBLE=8 $(WARNINGS) -DMEDNAFEN_VERSION=\"0.9.47\" -DPACKAGE=\"mednafen\" -DMEDNAFEN_VERSION_NUMERIC=947 -DPSS_STYLE=1 -DMPC_FIXED_POINT $(CORE_DEFINE) -DSTDC_HEADERS -D__STDC_LIMIT_MACROS -D__LIBRETRO__ -D_LOW_ACCURACY_ $(EXTRA_INCLUDES) $(SOUND_DEFINE)
FLAGS += $(ENDIANNESS_DEFINES) -DSIZEOF_DOUBLE=8 $(WARNINGS) -DMEDNAFEN_VERSION=\"1.24.0\" -DPACKAGE=\"mednafen\" -DMEDNAFEN_VERSION_NUMERIC=1240 -DPSS_STYLE=1 -DMPC_FIXED_POINT $(CORE_DEFINE) -DSTDC_HEADERS -D__STDC_LIMIT_MACROS -D__LIBRETRO__ -D_LOW_ACCURACY_ $(EXTRA_INCLUDES) $(SOUND_DEFINE)
ifneq ($(SANITIZER),)
CFLAGS += -fsanitize=$(SANITIZER)

View File

@ -19,7 +19,8 @@ SOURCES_CXX += \
$(CORE_EMU_DIR)/mikie.cpp \
$(CORE_EMU_DIR)/ram.cpp \
$(CORE_EMU_DIR)/rom.cpp \
$(CORE_EMU_DIR)/susie.cpp
$(CORE_EMU_DIR)/susie.cpp \
$(CORE_EMU_DIR)/system.cpp
endif
ifeq ($(NEED_BLIP), 1)
@ -72,6 +73,10 @@ ifeq ($(FRONTEND_SUPPORTS_RGB565), 1)
FLAGS += -DFRONTEND_SUPPORTS_RGB565
endif
ifneq ($(HAVE_NO_LANGEXTRA), 1)
FLAGS += -DHAVE_NO_LANGEXTRA
endif
ifneq ($(HAVE_GRIFFIN), 1)
SOURCES_CXX += \
$(MEDNAFEN_DIR)/mednafen.cpp \

View File

@ -23,10 +23,7 @@
/* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */
#ifdef _MSC_VER
#include <retro_common.h>
#if _MSC_VER >= 1800
#include <stdio.h> /* added for _vsnprintf_s and _vscprintf on VS2015 and VS2017 */
#endif
#include <stdio.h>
#include <stdarg.h>
#if _MSC_VER < 1800
@ -54,11 +51,14 @@ int c99_vsnprintf_retro__(char *outBuf, size_t size, const char *format, va_list
int count = -1;
if (size != 0)
{
#if (_MSC_VER <= 1310)
count = _vsnprintf(outBuf, size - 1, format, ap);
count = _vsnprintf(outBuf, size - 1, format, ap);
#else
count = _vsnprintf_s(outBuf, size, size - 1, format, ap);
count = _vsnprintf_s(outBuf, size, size - 1, format, ap);
#endif
}
if (count == -1)
count = _vscprintf(format, ap);

View File

@ -36,9 +36,7 @@
void *fopen_utf8(const char * filename, const char * mode)
{
#if defined(_XBOX)
return fopen(filename, mode);
#elif defined(LEGACY_WIN32)
#if defined(LEGACY_WIN32)
FILE *ret = NULL;
char * filename_local = utf8_to_local_string_alloc(filename);

View File

@ -211,10 +211,7 @@ size_t utf8len(const char *string)
return ret;
}
static uint8_t utf8_walkbyte(const char **string)
{
return *((*string)++);
}
#define utf8_walkbyte(string) (*((*(string))++))
/* Does not validate the input, returns garbage if it's not UTF-8. */
uint32_t utf8_walk(const char **string)
@ -227,14 +224,16 @@ uint32_t utf8_walk(const char **string)
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xE0)
{
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xF0)
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xF0)
return ret | (first & 7) << 18;
if (first >= 0xE0)
if (first >= 0xF0)
{
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
return ret | (first & 7) << 18;
}
return ret | (first & 15) << 12;
}
return ret | (first & 31) << 6;
}
@ -273,37 +272,25 @@ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
return ret;
}
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
/* Returned pointer MUST be freed by the caller if non-NULL. */
static char* mb_to_mb_string_alloc(const char *str,
static char *mb_to_mb_string_alloc(const char *str,
enum CodePage cp_in, enum CodePage cp_out)
{
char *path_buf = NULL;
wchar_t *path_buf_wide = NULL;
int path_buf_len = 0;
int path_buf_wide_len = 0;
int path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0);
if (!str || !*str)
return NULL;
(void)path_buf;
(void)path_buf_wide;
(void)path_buf_len;
(void)path_buf_wide_len;
#if !defined(_WIN32) || defined(_XBOX)
/* assume string needs no modification if not on Windows */
return strdup(str);
#else
#ifdef UNICODE
/* TODO/FIXME: Not implemented. */
return strdup(str);
#else
/* Windows 95 will return 0 from these functions with a UTF8 codepage set without MSLU. From an unknown MSDN version (others omit this info):
* - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-8. When this is set, dwFlags must be zero.
* - Windows 95: Under the Microsoft Layer for Unicode, MultiByteToWideChar also supports CP_UTF7 and CP_UTF8.
/* Windows 95 will return 0 from these functions with
* a UTF8 codepage set without MSLU.
*
* From an unknown MSDN version (others omit this info):
* - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later:
* Translate using UTF-8. When this is set, dwFlags must be zero.
* - Windows 95: Under the Microsoft Layer for Unicode,
* MultiByteToWideChar also supports CP_UTF7 and CP_UTF8.
*/
path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0);
if (path_buf_wide_len)
{
@ -355,20 +342,37 @@ static char* mb_to_mb_string_alloc(const char *str,
free(path_buf_wide);
return NULL;
#endif
#endif
}
#endif
/* Returned pointer MUST be freed by the caller if non-NULL. */
char* utf8_to_local_string_alloc(const char *str)
{
return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
if (str && *str)
{
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
#else
/* assume string needs no modification if not on Windows */
return strdup(str);
#endif
}
return NULL;
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
char* local_to_utf8_string_alloc(const char *str)
{
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
if (str && *str)
{
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
#else
/* assume string needs no modification if not on Windows */
return strdup(str);
#endif
}
return NULL;
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
@ -447,52 +451,44 @@ wchar_t* utf8_to_utf16_string_alloc(const char *str)
char* utf16_to_utf8_string_alloc(const wchar_t *str)
{
#ifdef _WIN32
int len = 0;
int out_len = 0;
int len = 0;
#else
size_t len = 0;
size_t out_len = 0;
size_t len = 0;
#endif
char *buf = NULL;
char *buf = NULL;
if (!str || !*str)
return NULL;
#ifdef _WIN32
len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
if (len)
{
UINT code_page = CP_UTF8;
len = WideCharToMultiByte(code_page,
0, str, -1, NULL, 0, NULL, NULL);
/* fallback to ANSI codepage instead */
if (!len)
{
code_page = CP_ACP;
len = WideCharToMultiByte(code_page,
0, str, -1, NULL, 0, NULL, NULL);
}
buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL;
out_len = WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, len, NULL, NULL);
}
else
{
/* fallback to ANSI codepage instead */
len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
if (len)
if (WideCharToMultiByte(code_page,
0, str, -1, buf, len, NULL, NULL) < 0)
{
buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL;
out_len = WideCharToMultiByte(CP_ACP, 0, str, -1, buf, len, NULL, NULL);
free(buf);
return NULL;
}
}
if (out_len < 0)
{
free(buf);
return NULL;
}
#else
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
/* NOTE: For now, assume non-Windows platforms'
* locale is already UTF-8. */
len = wcstombs(NULL, str, 0) + 1;
if (len)
@ -502,13 +498,11 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
if (!buf)
return NULL;
out_len = wcstombs(buf, str, len);
}
if (out_len == (size_t)-1)
{
free(buf);
return NULL;
if (wcstombs(buf, str, len) == (size_t)-1)
{
free(buf);
return NULL;
}
}
#endif

View File

@ -78,11 +78,6 @@
#include <unistd.h>
#endif
#if defined(ORBIS)
#include <orbisFile.h>
#include <sys/fcntl.h>
#include <sys/dirent.h>
#endif
#if defined(PSP)
#include <pspkernel.h>
#endif
@ -100,7 +95,7 @@
#define FIO_S_ISDIR SCE_S_ISDIR
#endif
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) || defined(PS2)
#include <unistd.h> /* stat() is defined here */
#endif
@ -119,17 +114,16 @@
#endif
static retro_vfs_stat_t path_stat_cb = NULL;
static retro_vfs_mkdir_t path_mkdir_cb = NULL;
static retro_vfs_stat_t path_stat_cb = retro_vfs_stat_impl;
static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl;
void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
{
const struct retro_vfs_interface* vfs_iface;
const struct retro_vfs_interface*
vfs_iface = vfs_info->iface;
path_stat_cb = NULL;
path_mkdir_cb = NULL;
vfs_iface = vfs_info->iface;
path_stat_cb = retro_vfs_stat_impl;
path_mkdir_cb = retro_vfs_mkdir_impl;
if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface)
return;
@ -138,18 +132,9 @@ void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
path_mkdir_cb = vfs_iface->mkdir;
}
static int path_stat(const char *path, int32_t *size)
int path_stat(const char *path)
{
if (path_stat_cb != NULL)
return path_stat_cb(path, size);
return retro_vfs_stat_impl(path, size);
}
static int path_mkdir_norecurse(const char *dir)
{
if (path_mkdir_cb != NULL)
return path_mkdir_cb(dir);
return retro_vfs_mkdir_impl(dir);
return path_stat_cb(path, NULL);
}
/**
@ -162,35 +147,23 @@ static int path_mkdir_norecurse(const char *dir)
*/
bool path_is_directory(const char *path)
{
#ifdef ORBIS
/* TODO: This should be moved to the VFS module */
int dfd;
if (!path)
return false;
dfd = orbisDopen(path);
if (dfd < 0)
return false;
orbisDclose(dfd);
return true;
#else
return (path_stat(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
#endif
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
}
bool path_is_character_special(const char *path)
{
return (path_stat(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
}
bool path_is_valid(const char *path)
{
return (path_stat(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
}
int32_t path_get_size(const char *path)
{
int32_t filesize = 0;
if (path_stat(path, &filesize) != 0)
if (path_stat_cb(path, &filesize) != 0)
return filesize;
return -1;
@ -206,56 +179,65 @@ int32_t path_get_size(const char *path)
**/
bool path_mkdir(const char *dir)
{
/* Use heap. Real chance of stack overflow if we recurse too hard. */
const char *target = NULL;
bool sret = false;
bool norecurse = false;
char *basedir = NULL;
if (dir && *dir)
basedir = strdup(dir);
if (!basedir)
if (!(dir && *dir))
return false;
/* Use heap. Real chance of stack
* overflow if we recurse too hard. */
basedir = strdup(dir);
if (!basedir)
return false;
path_parent_dir(basedir);
if (!*basedir || !strcmp(basedir, dir))
goto end;
{
free(basedir);
return false;
}
#if defined(GEKKO)
{
size_t len = strlen(basedir);
/* path_parent_dir() keeps the trailing slash.
* On Wii, mkdir() fails if the path has a
* trailing slash...
* We must therefore remove it. */
if (len > 0)
if (basedir[len - 1] == '/')
basedir[len - 1] = '\0';
}
#endif
if (path_is_directory(basedir))
{
target = dir;
norecurse = true;
}
else
{
target = basedir;
sret = path_mkdir(basedir);
if (sret)
{
target = dir;
norecurse = true;
}
}
free(basedir);
if (norecurse)
{
int ret = path_mkdir_norecurse(dir);
int ret = path_mkdir_cb(dir);
/* Don't treat this as an error. */
if (ret == -2 && path_is_directory(dir))
ret = 0;
return true;
if (ret < 0)
printf("mkdir(%s) error: %s.\n", dir, strerror(errno));
sret = (ret == 0);
return (ret == 0);
}
end:
if (target && !sret)
printf("Failed to create directory: \"%s\".\n", target);
free(basedir);
return sret;
}
@ -274,19 +256,20 @@ const char *path_get_archive_delim(const char *path)
const char *last = find_last_slash(path);
const char *delim = NULL;
if (last)
{
delim = strcasestr(last, ".zip#");
if (!last)
return NULL;
if (!delim)
delim = strcasestr(last, ".apk#");
}
/* Test if it's .zip */
delim = strcasestr(last, ".zip#");
if (!delim) /* If it's not a .zip, test if it's .apk */
delim = strcasestr(last, ".apk#");
if (delim)
return delim + 4;
if (last)
delim = strcasestr(last, ".7z#");
/* If it's not a .zip or .apk file, test if it's .7z */
delim = strcasestr(last, ".7z#");
if (delim)
return delim + 3;
@ -305,11 +288,10 @@ const char *path_get_archive_delim(const char *path)
*/
const char *path_get_extension(const char *path)
{
const char *ext = !string_is_empty(path)
? strrchr(path_basename(path), '.') : NULL;
if (!ext)
return "";
return ext + 1;
const char *ext;
if (!string_is_empty(path) && ((ext = strrchr(path_basename(path), '.'))))
return ext + 1;
return "";
}
/**
@ -407,11 +389,11 @@ void fill_pathname(char *out_path, const char *in_path,
* present in 'in_path', it will be ignored.
*
*/
void fill_pathname_noext(char *out_path, const char *in_path,
size_t fill_pathname_noext(char *out_path, const char *in_path,
const char *replace, size_t size)
{
strlcpy(out_path, in_path, size);
strlcat(out_path, replace, size);
return strlcat(out_path, replace, size);
}
char *find_last_slash(const char *str)
@ -420,10 +402,9 @@ char *find_last_slash(const char *str)
#ifdef _WIN32
const char *backslash = strrchr(str, '\\');
if (backslash && ((slash && backslash > slash) || !slash))
slash = backslash;
if (!slash || (backslash > slash))
return (char*)backslash;
#endif
return (char*)slash;
}
@ -437,21 +418,22 @@ char *find_last_slash(const char *str)
**/
void fill_pathname_slash(char *path, size_t size)
{
size_t path_len = strlen(path);
size_t path_len;
const char *last_slash = find_last_slash(path);
/* Try to preserve slash type. */
if (last_slash && (last_slash != (path + path_len - 1)))
if (!last_slash)
{
char join_str[2];
join_str[0] = '\0';
strlcpy(join_str, last_slash, sizeof(join_str));
strlcat(path, join_str, size);
}
else if (!last_slash)
strlcat(path, path_default_slash(), size);
return;
}
path_len = strlen(path);
/* Try to preserve slash type. */
if (last_slash != (path + path_len - 1))
{
path[path_len] = last_slash[0];
path[path_len+1] = '\0';
}
}
/**
@ -471,7 +453,7 @@ void fill_pathname_slash(char *path, size_t size)
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
**/
void fill_pathname_dir(char *in_dir, const char *in_basename,
size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size)
{
const char *base = NULL;
@ -479,7 +461,7 @@ void fill_pathname_dir(char *in_dir, const char *in_basename,
fill_pathname_slash(in_dir, size);
base = path_basename(in_basename);
strlcat(in_dir, base, size);
strlcat(in_dir, replace, size);
return strlcat(in_dir, replace, size);
}
/**
@ -490,27 +472,29 @@ void fill_pathname_dir(char *in_dir, const char *in_basename,
*
* Copies basename of @in_path into @out_path.
**/
void fill_pathname_base(char *out, const char *in_path, size_t size)
size_t fill_pathname_base(char *out, const char *in_path, size_t size)
{
const char *ptr = path_basename(in_path);
if (!ptr)
ptr = in_path;
strlcpy(out, ptr, size);
return strlcpy(out, ptr, size);
}
void fill_pathname_base_noext(char *out, const char *in_path, size_t size)
void fill_pathname_base_noext(char *out,
const char *in_path, size_t size)
{
fill_pathname_base(out, in_path, size);
path_remove_extension(out);
}
void fill_pathname_base_ext(char *out, const char *in_path, const char *ext,
size_t fill_pathname_base_ext(char *out,
const char *in_path, const char *ext,
size_t size)
{
fill_pathname_base_noext(out, in_path, size);
strlcat(out, ext, size);
return strlcat(out, ext, size);
}
/**
@ -551,25 +535,28 @@ void fill_pathname_basedir_noext(char *out_dir,
bool fill_pathname_parent_dir_name(char *out_dir,
const char *in_dir, size_t size)
{
char *temp = strdup(in_dir);
char *last = find_last_slash(temp);
bool ret = false;
bool success = false;
char *temp = strdup(in_dir);
char *last = find_last_slash(temp);
*last = '\0';
in_dir = find_last_slash(temp);
if (in_dir && in_dir + 1)
if (last && last[1] == 0)
{
strlcpy(out_dir, in_dir + 1, size);
ret = true;
*last = '\0';
last = find_last_slash(temp);
}
else
ret = false;
if (last)
*last = '\0';
in_dir = find_last_slash(temp);
success = in_dir && in_dir[1];
if (success)
strlcpy(out_dir, in_dir + 1, size);
free(temp);
return ret;
return success;
}
/**
@ -602,14 +589,15 @@ void fill_pathname_parent_dir(char *out_dir,
* E.g.:
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
**/
void fill_dated_filename(char *out_filename,
size_t fill_dated_filename(char *out_filename,
const char *ext, size_t size)
{
time_t cur_time = time(NULL);
time_t cur_time = time(NULL);
const struct tm* tm_ = localtime(&cur_time);
strftime(out_filename, size,
"RetroArch-%m%d-%H%M%S.", localtime(&cur_time));
strlcat(out_filename, ext, size);
"RetroArch-%m%d-%H%M%S", tm_);
return strlcat(out_filename, ext, size);
}
/**
@ -629,15 +617,24 @@ void fill_str_dated_filename(char *out_filename,
const char *in_str, const char *ext, size_t size)
{
char format[256];
time_t cur_time = time(NULL);
time_t cur_time = time(NULL);
const struct tm* tm_ = localtime(&cur_time);
format[0] = '\0';
format[0] = '\0';
strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", localtime(&cur_time));
if (string_is_empty(ext))
{
strftime(format, sizeof(format), "-%y%m%d-%H%M%S", tm_);
fill_pathname_noext(out_filename, in_str, format, size);
}
else
{
strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", tm_);
fill_pathname_join_concat_noext(out_filename,
in_str, format, ext,
size);
fill_pathname_join_concat_noext(out_filename,
in_str, format, ext,
size);
}
}
/**
@ -650,6 +647,7 @@ void fill_str_dated_filename(char *out_filename,
void path_basedir(char *path)
{
char *last = NULL;
if (strlen(path) < 2)
return;
@ -671,12 +669,20 @@ void path_basedir(char *path)
**/
void path_parent_dir(char *path)
{
bool path_was_absolute = path_is_absolute(path);
size_t len = strlen(path);
size_t len = 0;
if (!path)
return;
len = strlen(path);
if (len && path_char_is_slash(path[len - 1]))
{
bool path_was_absolute = path_is_absolute(path);
path[len - 1] = '\0';
if (path_was_absolute && find_last_slash(path) == NULL)
if (path_was_absolute && !find_last_slash(path))
{
/* We removed the only slash from what used to be an absolute path.
* On Linux, this goes from "/" to an empty string and everything works fine,
@ -700,16 +706,17 @@ void path_parent_dir(char *path)
**/
const char *path_basename(const char *path)
{
/* We cut either at the first compression-related hash
* or the last slash; whichever comes last */
const char *last = find_last_slash(path);
/* We cut at the first compression-related hash */
const char *delim = path_get_archive_delim(path);
if (delim)
return delim + 1;
if (last)
return last + 1;
{
/* We cut at the last slash */
const char *last = find_last_slash(path);
if (last)
return last + 1;
}
return path;
}
@ -742,34 +749,174 @@ bool path_is_absolute(const char *path)
/**
* path_resolve_realpath:
* @buf : buffer for path
* @buf : input and output buffer for path
* @size : size of buffer
* @resolve_symlinks : whether to resolve symlinks or not
*
* Turns relative paths into absolute path.
* If relative, rebases on current working dir.
* Resolves use of ".", "..", multiple slashes etc in absolute paths.
*
* Relative paths are rebased on the current working dir.
*
* Returns: @buf if successful, NULL otherwise.
* Note: Not implemented on consoles
* Note: Symlinks are only resolved on Unix-likes
* Note: The current working dir might not be what you expect,
* e.g. on Android it is "/"
* Use of fill_pathname_resolve_relative() should be prefered
**/
void path_resolve_realpath(char *buf, size_t size)
char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
{
#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
char tmp[PATH_MAX_LENGTH];
tmp[0] = '\0';
#ifdef _WIN32
strlcpy(tmp, buf, sizeof(tmp));
if (!_fullpath(buf, tmp, size))
{
strlcpy(buf, tmp, size);
return NULL;
}
return buf;
#else
size_t t;
char *p;
const char *next;
const char *buf_end;
if (resolve_symlinks)
{
strlcpy(tmp, buf, sizeof(tmp));
/* NOTE: realpath() expects at least PATH_MAX_LENGTH bytes in buf.
* Technically, PATH_MAX_LENGTH needn't be defined, but we rely on it anyways.
* POSIX 2008 can automatically allocate for you,
* but don't rely on that. */
if (!realpath(tmp, buf))
{
strlcpy(buf, tmp, size);
return NULL;
}
return buf;
}
t = 0; /* length of output */
buf_end = buf + strlen(buf);
if (!path_is_absolute(buf))
{
size_t len;
/* rebase on working directory */
if (!getcwd(tmp, PATH_MAX_LENGTH-1))
return NULL;
len = strlen(tmp);
t += len;
if (tmp[len-1] != '/')
tmp[t++] = '/';
if (string_is_empty(buf))
goto end;
p = buf;
}
else
{
/* UNIX paths can start with multiple '/', copy those */
for (p = buf; *p == '/'; p++)
tmp[t++] = '/';
}
/* p points to just after a slash while 'next' points to the next slash
* if there are no slashes, they point relative to where one would be */
do
{
next = strchr(p, '/');
if (!next)
next = buf_end;
if ((next - p == 2 && p[0] == '.' && p[1] == '.'))
{
p += 3;
/* fail for illegal /.., //.. etc */
if (t == 1 || tmp[t-2] == '/')
return NULL;
/* delete previous segment in tmp by adjusting size t
* tmp[t-1] == '/', find '/' before that */
t = t-2;
while (tmp[t] != '/')
t--;
t++;
}
else if (next - p == 1 && p[0] == '.')
p += 2;
else if (next - p == 0)
p += 1;
else
{
/* fail when truncating */
if (t + next-p+1 > PATH_MAX_LENGTH-1)
return NULL;
while (p <= next)
tmp[t++] = *p++;
}
}
while (next < buf_end);
end:
tmp[t] = '\0';
strlcpy(buf, tmp, size);
return buf;
#endif
#endif
return NULL;
}
/**
* path_relative_to:
* @out : buffer to write the relative path to
* @path : path to be expressed relatively
* @base : base directory to start out on
* @size : size of output buffer
*
* Turns @path into a path relative to @base and writes it to @out.
*
* @base is assumed to be a base directory, i.e. a path ending with '/' or '\'.
* Both @path and @base are assumed to be absolute paths without "." or "..".
*
* E.g. path /a/b/e/f.cg with base /a/b/c/d/ turns into ../../e/f.cg
**/
size_t path_relative_to(char *out,
const char *path, const char *base, size_t size)
{
size_t i;
const char *trimmed_path, *trimmed_base;
#ifdef _WIN32
if (!_fullpath(buf, tmp, size))
strlcpy(buf, tmp, size);
#else
/* For different drives, return absolute path */
if (strlen(path) >= 2 && strlen(base) >= 2
&& path[1] == ':' && base[1] == ':'
&& path[0] != base[0])
return strlcpy(out, path, size);
#endif
/* NOTE: realpath() expects at least PATH_MAX_LENGTH bytes in buf.
* Technically, PATH_MAX_LENGTH needn't be defined, but we rely on it anyways.
* POSIX 2008 can automatically allocate for you,
* but don't rely on that. */
if (!realpath(tmp, buf))
strlcpy(buf, tmp, size);
#endif
#endif
/* Trim common beginning */
for (i = 0; path[i] && base[i] && path[i] == base[i]; )
i++;
trimmed_path = path+i;
trimmed_base = base+i;
/* Each segment of base turns into ".." */
out[0] = '\0';
for (i = 0; trimmed_base[i]; i++)
if (trimmed_base[i] == path_default_slash_c())
strlcat(out, ".." path_default_slash(), size);
return strlcat(out, trimmed_path, size);
}
/**
@ -795,6 +942,7 @@ void fill_pathname_resolve_relative(char *out_path,
fill_pathname_basedir(out_path, in_refpath, size);
strlcat(out_path, in_path, size);
path_resolve_realpath(out_path, size, false);
}
/**
@ -808,7 +956,7 @@ void fill_pathname_resolve_relative(char *out_path,
* Makes sure not to get two consecutive slashes
* between directory and path.
**/
void fill_pathname_join(char *out_path,
size_t fill_pathname_join(char *out_path,
const char *dir, const char *path, size_t size)
{
if (out_path != dir)
@ -817,10 +965,10 @@ void fill_pathname_join(char *out_path,
if (*out_path)
fill_pathname_slash(out_path, size);
strlcat(out_path, path, size);
return strlcat(out_path, path, size);
}
void fill_pathname_join_special_ext(char *out_path,
size_t fill_pathname_join_special_ext(char *out_path,
const char *dir, const char *path,
const char *last, const char *ext,
size_t size)
@ -830,26 +978,25 @@ void fill_pathname_join_special_ext(char *out_path,
fill_pathname_slash(out_path, size);
strlcat(out_path, last, size);
strlcat(out_path, ext, size);
return strlcat(out_path, ext, size);
}
void fill_pathname_join_concat_noext(
char *out_path,
size_t fill_pathname_join_concat_noext(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size)
{
fill_pathname_noext(out_path, dir, path, size);
strlcat(out_path, concat, size);
return strlcat(out_path, concat, size);
}
void fill_pathname_join_concat(char *out_path,
size_t fill_pathname_join_concat(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size)
{
fill_pathname_join(out_path, dir, path, size);
strlcat(out_path, concat, size);
return strlcat(out_path, concat, size);
}
void fill_pathname_join_noext(char *out_path,
@ -870,7 +1017,7 @@ void fill_pathname_join_noext(char *out_path,
* Joins a directory (@dir) and path (@path) together
* using the given delimiter (@delim).
**/
void fill_pathname_join_delim(char *out_path, const char *dir,
size_t fill_pathname_join_delim(char *out_path, const char *dir,
const char *path, const char delim, size_t size)
{
size_t copied;
@ -884,15 +1031,16 @@ void fill_pathname_join_delim(char *out_path, const char *dir,
out_path[copied+1] = '\0';
if (path)
strlcat(out_path, path, size);
copied = strlcat(out_path, path, size);
return copied;
}
void fill_pathname_join_delim_concat(char *out_path, const char *dir,
size_t fill_pathname_join_delim_concat(char *out_path, const char *dir,
const char *path, const char delim, const char *concat,
size_t size)
{
fill_pathname_join_delim(out_path, dir, path, delim, size);
strlcat(out_path, concat, size);
return strlcat(out_path, concat, size);
}
/**
@ -910,7 +1058,7 @@ void fill_pathname_join_delim_concat(char *out_path, const char *dir,
* E.g.: "/path/to/game.img" -> game.img
* "/path/to/myarchive.7z#folder/to/game.img" -> game.img
*/
void fill_short_pathname_representation(char* out_rep,
size_t fill_short_pathname_representation(char* out_rep,
const char *in_path, size_t size)
{
char path_short[PATH_MAX_LENGTH];
@ -920,7 +1068,7 @@ void fill_short_pathname_representation(char* out_rep,
fill_pathname(path_short, path_basename(in_path), "",
sizeof(path_short));
strlcpy(out_rep, path_short, size);
return strlcpy(out_rep, path_short, size);
}
void fill_short_pathname_representation_noext(char* out_rep,
@ -988,7 +1136,7 @@ void fill_pathname_expand_special(char *out_path,
retro_assert(src_size < size);
out_path += src_size;
size -= src_size;
size -= src_size;
}
in_path += 2;
@ -1134,13 +1282,30 @@ void fill_pathname_application_path(char *s, size_t len)
#elif defined(__APPLE__)
if (bundle)
{
CFURLRef bundle_url = CFBundleCopyBundleURL(bundle);
CFURLRef bundle_url = CFBundleCopyBundleURL(bundle);
CFStringRef bundle_path = CFURLCopyPath(bundle_url);
CFStringGetCString(bundle_path, s, len, kCFStringEncodingUTF8);
#ifdef HAVE_COCOATOUCH
{
/* This needs to be done so that the path becomes
* /private/var/... and this
* is used consistently throughout for the iOS bundle path */
char resolved_bundle_dir_buf[PATH_MAX_LENGTH] = {0};
if (realpath(s, resolved_bundle_dir_buf))
{
strlcpy(s, resolved_bundle_dir_buf, len - 1);
strlcat(s, "/", len);
}
}
#endif
CFRelease(bundle_path);
CFRelease(bundle_url);
#ifndef HAVE_COCOATOUCH
/* Not sure what this does but it breaks
* stuff for iOS, so skipping */
retro_assert(strlcat(s, "nobin", len) < len);
#endif
return;
}
#elif defined(__HAIKU__)
@ -1155,7 +1320,7 @@ void fill_pathname_application_path(char *s, size_t len)
#elif defined(__QNX__)
char *buff = malloc(len);
if(_cmdname(buff))
if (_cmdname(buff))
strlcpy(s, buff, len);
free(buff);
@ -1210,3 +1375,20 @@ void fill_pathname_home_dir(char *s, size_t len)
#endif
}
#endif
bool is_path_accessible_using_standard_io(const char *path)
{
#ifdef __WINRT__
bool result;
size_t path_sizeof = PATH_MAX_LENGTH * sizeof(char);
char *relative_path_abbrev = (char*)malloc(path_sizeof);
fill_pathname_abbreviate_special(relative_path_abbrev, path, path_sizeof);
result = strlen(relative_path_abbrev) >= 2 && (relative_path_abbrev[0] == ':' || relative_path_abbrev[0] == '~') && path_char_is_slash(relative_path_abbrev[1]);
free(relative_path_abbrev);
return result;
#else
return true;
#endif
}

View File

@ -56,6 +56,8 @@ extern "C" {
#undef UNICODE /* Do not bother with UNICODE at this time. */
#include <direct.h>
#include <stddef.h>
#define _USE_MATH_DEFINES
#include <math.h>
/* Python headers defines ssize_t and sets HAVE_SSIZE_T.

View File

@ -147,13 +147,38 @@ void path_parent_dir(char *path);
/**
* path_resolve_realpath:
* @buf : buffer for path
* @buf : input and output buffer for path
* @size : size of buffer
* @resolve_symlinks : whether to resolve symlinks or not
*
* Turns relative paths into absolute path.
* If relative, rebases on current working dir.
* Resolves use of ".", "..", multiple slashes etc in absolute paths.
*
* Relative paths are rebased on the current working dir.
*
* Returns: @buf if successful, NULL otherwise.
* Note: Not implemented on consoles
* Note: Symlinks are only resolved on Unix-likes
* Note: The current working dir might not be what you expect,
* e.g. on Android it is "/"
* Use of fill_pathname_resolve_relative() should be prefered
**/
void path_resolve_realpath(char *buf, size_t size);
char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
/**
* path_relative_to:
* @out : buffer to write the relative path to
* @path : path to be expressed relatively
* @base : relative to this
* @size : size of output buffer
*
* Turns @path into a path relative to @base and writes it to @out.
*
* @base is assumed to be a base directory, i.e. a path ending with '/' or '\'.
* Both @path and @base are assumed to be absolute paths without "." or "..".
*
* E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
**/
size_t path_relative_to(char *out, const char *path, const char *base, size_t size);
/**
* path_is_absolute:
@ -201,7 +226,7 @@ void fill_pathname(char *out_path, const char *in_path,
* E.g.:
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
**/
void fill_dated_filename(char *out_filename,
size_t fill_dated_filename(char *out_filename,
const char *ext, size_t size);
/**
@ -234,7 +259,7 @@ void fill_str_dated_filename(char *out_filename,
* present in 'in_path', it will be ignored.
*
*/
void fill_pathname_noext(char *out_path, const char *in_path,
size_t fill_pathname_noext(char *out_path, const char *in_path,
const char *replace, size_t size);
/**
@ -264,7 +289,7 @@ char *find_last_slash(const char *str);
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
**/
void fill_pathname_dir(char *in_dir, const char *in_basename,
size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size);
/**
@ -275,12 +300,12 @@ void fill_pathname_dir(char *in_dir, const char *in_basename,
*
* Copies basename of @in_path into @out_path.
**/
void fill_pathname_base(char *out_path, const char *in_path, size_t size);
size_t fill_pathname_base(char *out_path, const char *in_path, size_t size);
void fill_pathname_base_noext(char *out_dir,
const char *in_path, size_t size);
void fill_pathname_base_ext(char *out,
size_t fill_pathname_base_ext(char *out,
const char *in_path, const char *ext,
size_t size);
@ -351,21 +376,20 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
* Makes sure not to get two consecutive slashes
* between directory and path.
**/
void fill_pathname_join(char *out_path, const char *dir,
size_t fill_pathname_join(char *out_path, const char *dir,
const char *path, size_t size);
void fill_pathname_join_special_ext(char *out_path,
size_t fill_pathname_join_special_ext(char *out_path,
const char *dir, const char *path,
const char *last, const char *ext,
size_t size);
void fill_pathname_join_concat_noext(
char *out_path,
size_t fill_pathname_join_concat_noext(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size);
void fill_pathname_join_concat(char *out_path,
size_t fill_pathname_join_concat(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size);
@ -384,10 +408,10 @@ void fill_pathname_join_noext(char *out_path,
* Joins a directory (@dir) and path (@path) together
* using the given delimiter (@delim).
**/
void fill_pathname_join_delim(char *out_path, const char *dir,
size_t fill_pathname_join_delim(char *out_path, const char *dir,
const char *path, const char delim, size_t size);
void fill_pathname_join_delim_concat(char *out_path, const char *dir,
size_t fill_pathname_join_delim_concat(char *out_path, const char *dir,
const char *path, const char delim, const char *concat,
size_t size);
@ -406,7 +430,7 @@ void fill_pathname_join_delim_concat(char *out_path, const char *dir,
* E.g.: "/path/to/game.img" -> game.img
* "/path/to/myarchive.7z#folder/to/game.img" -> game.img
*/
void fill_short_pathname_representation(char* out_rep,
size_t fill_short_pathname_representation(char* out_rep,
const char *in_path, size_t size);
void fill_short_pathname_representation_noext(char* out_rep,
@ -494,10 +518,14 @@ bool path_is_directory(const char *path);
bool path_is_character_special(const char *path);
int path_stat(const char *path);
bool path_is_valid(const char *path);
int32_t path_get_size(const char *path);
bool is_path_accessible_using_standard_io(const char *path);
RETRO_END_DECLS
#endif

View File

@ -202,6 +202,8 @@ extern "C" {
#define RETRO_DEVICE_ID_JOYPAD_L3 14
#define RETRO_DEVICE_ID_JOYPAD_R3 15
#define RETRO_DEVICE_ID_JOYPAD_MASK 256
/* Index / Id values for ANALOG device. */
#define RETRO_DEVICE_INDEX_ANALOG_LEFT 0
#define RETRO_DEVICE_INDEX_ANALOG_RIGHT 1
@ -248,6 +250,7 @@ extern "C" {
#define RETRO_DEVICE_ID_POINTER_X 0
#define RETRO_DEVICE_ID_POINTER_Y 1
#define RETRO_DEVICE_ID_POINTER_PRESSED 2
#define RETRO_DEVICE_ID_POINTER_COUNT 3
/* Returned from retro_get_region(). */
#define RETRO_REGION_NTSC 0
@ -274,6 +277,7 @@ enum retro_language
RETRO_LANGUAGE_VIETNAMESE = 15,
RETRO_LANGUAGE_ARABIC = 16,
RETRO_LANGUAGE_GREEK = 17,
RETRO_LANGUAGE_TURKISH = 18,
RETRO_LANGUAGE_LAST,
/* Ensure sizeof(enum) == sizeof(int) */
@ -485,11 +489,13 @@ enum retro_mod
/* Environment commands. */
#define RETRO_ENVIRONMENT_SET_ROTATION 1 /* const unsigned * --
* Sets screen rotation of graphics.
* Is only implemented if rotation can be accelerated by hardware.
* Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180,
* 270 degrees counter-clockwise respectively.
*/
#define RETRO_ENVIRONMENT_GET_OVERSCAN 2 /* bool * --
* NOTE: As of 2019 this callback is considered deprecated in favor of
* using core options to manage overscan in a more nuanced, core-specific way.
*
* Boolean value whether or not the implementation should use overscan,
* or crop away overscan.
*/
@ -608,7 +614,7 @@ enum retro_mod
* Afterward it may be called again for the core to communicate
* updated options to the frontend, but the number of core
* options must not change from the number in the initial call.
*
*
* 'data' points to an array of retro_variable structs
* terminated by a { NULL, NULL } element.
* retro_variable::key should be namespaced to not collide
@ -1079,10 +1085,183 @@ enum retro_mod
* fastforwarding mode.
*/
#define RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE (50 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/* float * --
* Float value that lets us know what target refresh rate
* is curently in use by the frontend.
*
* The core can use the returned value to set an ideal
* refresh rate/framerate.
*/
#define RETRO_ENVIRONMENT_GET_INPUT_BITMASKS (51 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/* bool * --
* Boolean value that indicates whether or not the frontend supports
* input bitmasks being returned by retro_input_state_t. The advantage
* of this is that retro_input_state_t has to be only called once to
* grab all button states instead of multiple times.
*
* If it returns true, you can pass RETRO_DEVICE_ID_JOYPAD_MASK as 'id'
* to retro_input_state_t (make sure 'device' is set to RETRO_DEVICE_JOYPAD).
* It will return a bitmask of all the digital buttons.
*/
#define RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION 52
/* unsigned * --
* Unsigned value is the API version number of the core options
* interface supported by the frontend. If callback return false,
* API version is assumed to be 0.
*
* In legacy code, core options are set by passing an array of
* retro_variable structs to RETRO_ENVIRONMENT_SET_VARIABLES.
* This may be still be done regardless of the core options
* interface version.
*
* If version is 1 however, core options may instead be set by
* passing an array of retro_core_option_definition structs to
* RETRO_ENVIRONMENT_SET_CORE_OPTIONS, or a 2D array of
* retro_core_option_definition structs to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL.
* This allows the core to additionally set option sublabel information
* and/or provide localisation support.
*/
#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS 53
/* const struct retro_core_option_definition ** --
* Allows an implementation to signal the environment
* which variables it might want to check for later using
* GET_VARIABLE.
* This allows the frontend to present these variables to
* a user dynamically.
* This should only be called if RETRO_ENVIRONMENT_GET_ENHANCED_CORE_OPTIONS
* returns an API version of 1.
* This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
* This should be called the first time as early as
* possible (ideally in retro_set_environment).
* Afterwards it may be called again for the core to communicate
* updated options to the frontend, but the number of core
* options must not change from the number in the initial call.
*
* 'data' points to an array of retro_core_option_definition structs
* terminated by a { NULL, NULL, NULL, {{0}}, NULL } element.
* retro_core_option_definition::key should be namespaced to not collide
* with other implementations' keys. e.g. A core called
* 'foo' should use keys named as 'foo_option'.
* retro_core_option_definition::desc should contain a human readable
* description of the key.
* retro_core_option_definition::info should contain any additional human
* readable information text that a typical user may need to
* understand the functionality of the option.
* retro_core_option_definition::values is an array of retro_core_option_value
* structs terminated by a { NULL, NULL } element.
* > retro_core_option_definition::values[index].value is an expected option
* value.
* > retro_core_option_definition::values[index].label is a human readable
* label used when displaying the value on screen. If NULL,
* the value itself is used.
* retro_core_option_definition::default_value is the default core option
* setting. It must match one of the expected option values in the
* retro_core_option_definition::values array. If it does not, or the
* default value is NULL, the first entry in the
* retro_core_option_definition::values array is treated as the default.
*
* The number of possible options should be very limited,
* and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX.
* i.e. it should be feasible to cycle through options
* without a keyboard.
*
* First entry should be treated as a default.
*
* Example entry:
* {
* "foo_option",
* "Speed hack coprocessor X",
* "Provides increased performance at the expense of reduced accuracy",
* {
* { "false", NULL },
* { "true", NULL },
* { "unstable", "Turbo (Unstable)" },
* { NULL, NULL },
* },
* "false"
* }
*
* Only strings are operated on. The possible values will
* generally be displayed and stored as-is by the frontend.
*/
#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL 54
/* const struct retro_core_options_intl * --
* Allows an implementation to signal the environment
* which variables it might want to check for later using
* GET_VARIABLE.
* This allows the frontend to present these variables to
* a user dynamically.
* This should only be called if RETRO_ENVIRONMENT_GET_ENHANCED_CORE_OPTIONS
* returns an API version of 1.
* This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
* This should be called the first time as early as
* possible (ideally in retro_set_environment).
* Afterwards it may be called again for the core to communicate
* updated options to the frontend, but the number of core
* options must not change from the number in the initial call.
*
* This is fundamentally the same as RETRO_ENVIRONMENT_SET_CORE_OPTIONS,
* with the addition of localisation support. The description of the
* RETRO_ENVIRONMENT_SET_CORE_OPTIONS callback should be consulted
* for further details.
*
* 'data' points to a retro_core_options_intl struct.
*
* retro_core_options_intl::us is a pointer to an array of
* retro_core_option_definition structs defining the US English
* core options implementation. It must point to a valid array.
*
* retro_core_options_intl::local is a pointer to an array of
* retro_core_option_definition structs defining core options for
* the current frontend language. It may be NULL (in which case
* retro_core_options_intl::us is used by the frontend). Any items
* missing from this array will be read from retro_core_options_intl::us
* instead.
*
* NOTE: Default core option values are always taken from the
* retro_core_options_intl::us array. Any default values in
* retro_core_options_intl::local array will be ignored.
*/
#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY 55
/* struct retro_core_option_display * --
*
* Allows an implementation to signal the environment to show
* or hide a variable when displaying core options. This is
* considered a *suggestion*. The frontend is free to ignore
* this callback, and its implementation not considered mandatory.
*
* 'data' points to a retro_core_option_display struct
*
* retro_core_option_display::key is a variable identifier
* which has already been set by SET_VARIABLES/SET_CORE_OPTIONS.
*
* retro_core_option_display::visible is a boolean, specifying
* whether variable should be displayed
*
* Note that all core option variables will be set visible by
* default when calling SET_VARIABLES/SET_CORE_OPTIONS.
*/
#define RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER 56
/* unsigned * --
*
* Allows an implementation to ask frontend preferred hardware
* context to use. Core should use this information to deal
* with what specific context to request with SET_HW_RENDER.
*
* 'data' points to an unsigned variable
*/
/* VFS functionality */
/* File paths:
* File paths passed as parameters when using this api shall be well formed unix-style,
* File paths passed as parameters when using this API shall be well formed UNIX-style,
* using "/" (unquoted forward slash) as directory separator regardless of the platform's native separator.
* Paths shall also include at least one forward slash ("game.bin" is an invalid path, use "./game.bin" instead).
* Other than the directory separator, cores shall not make assumptions about path format:
@ -1755,6 +1934,10 @@ enum retro_sensor_action
{
RETRO_SENSOR_ACCELEROMETER_ENABLE = 0,
RETRO_SENSOR_ACCELEROMETER_DISABLE,
RETRO_SENSOR_GYROSCOPE_ENABLE,
RETRO_SENSOR_GYROSCOPE_DISABLE,
RETRO_SENSOR_ILLUMINANCE_ENABLE,
RETRO_SENSOR_ILLUMINANCE_DISABLE,
RETRO_SENSOR_DUMMY = INT_MAX
};
@ -1763,6 +1946,10 @@ enum retro_sensor_action
#define RETRO_SENSOR_ACCELEROMETER_X 0
#define RETRO_SENSOR_ACCELEROMETER_Y 1
#define RETRO_SENSOR_ACCELEROMETER_Z 2
#define RETRO_SENSOR_GYROSCOPE_X 3
#define RETRO_SENSOR_GYROSCOPE_Y 4
#define RETRO_SENSOR_GYROSCOPE_Z 5
#define RETRO_SENSOR_ILLUMINANCE 6
typedef bool (RETRO_CALLCONV *retro_set_sensor_state_t)(unsigned port,
enum retro_sensor_action action, unsigned rate);
@ -2324,6 +2511,64 @@ struct retro_variable
const char *value;
};
struct retro_core_option_display
{
/* Variable to configure in RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY */
const char *key;
/* Specifies whether variable should be displayed
* when presenting core options to the user */
bool visible;
};
/* Maximum number of values permitted for a core option
* NOTE: This may be increased on a core-by-core basis
* if required (doing so has no effect on the frontend) */
#define RETRO_NUM_CORE_OPTION_VALUES_MAX 128
struct retro_core_option_value
{
/* Expected option value */
const char *value;
/* Human-readable value label. If NULL, value itself
* will be displayed by the frontend */
const char *label;
};
struct retro_core_option_definition
{
/* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. */
const char *key;
/* Human-readable core option description (used as menu label) */
const char *desc;
/* Human-readable core option information (used as menu sublabel) */
const char *info;
/* Array of retro_core_option_value structs, terminated by NULL */
struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX];
/* Default core option value. Must match one of the values
* in the retro_core_option_value array, otherwise will be
* ignored */
const char *default_value;
};
struct retro_core_options_intl
{
/* Pointer to an array of retro_core_option_definition structs
* - US English implementation
* - Must point to a valid array */
struct retro_core_option_definition *us;
/* Pointer to an array of retro_core_option_definition structs
* - Implementation for current frontend language
* - May be NULL */
struct retro_core_option_definition *local;
};
struct retro_game_info
{
const char *path; /* Path to game, UTF-8 encoded.

View File

@ -26,7 +26,7 @@
#include <stdio.h>
#include <stdint.h>
#if defined(__CELLOS_LV2__) || defined(PSP) || defined(PS2) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH)
#if defined(__CELLOS_LV2__) || defined(PSP) || defined(PS2) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(HAVE_LIBNX)
/* No mman available */
#elif defined(_WIN32) && !defined(_XBOX)
#include <windows.h>

View File

@ -34,4 +34,3 @@ in a public API, you may need this.
#include <compat/msvc.h>
#endif

View File

@ -89,7 +89,9 @@ typedef int ssize_t;
/* C++11 says this one isn't needed, but apparently (some versions of) mingw require it anyways */
/* https://stackoverflow.com/questions/8132399/how-to-printf-uint64-t-fails-with-spurious-trailing-in-format */
/* https://github.com/libretro/RetroArch/issues/6009 */
#define __STDC_FORMAT_MACROS
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS 1
#endif
#include <inttypes.h>
#endif
#ifndef PRId64

View File

@ -0,0 +1,114 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_environment.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_ENVIRONMENT_H
#define __LIBRETRO_SDK_ENVIRONMENT_H
/*
This file is designed to create a normalized environment for compiling
libretro-common's private implementations, or any other sources which might
enjoy use of it's environment (RetroArch for instance).
This should be an elaborately crafted environment so that sources don't
need to be full of platform-specific workarounds.
*/
#if defined (__cplusplus)
#if 0
printf("This is C++, version %d.\n", __cplusplus);
#endif
/* The expected values would be
* 199711L, for ISO/IEC 14882:1998 or 14882:2003
*/
#elif defined(__STDC__)
/* This is standard C. */
#if (__STDC__ == 1)
/* The implementation is ISO-conforming. */
#define __STDC_ISO__
#else
/* The implementation is not ISO-conforming. */
#endif
#if defined(__STDC_VERSION__)
#if (__STDC_VERSION__ >= 201112L)
/* This is C11. */
#define __STDC_C11__
#elif (__STDC_VERSION__ >= 199901L)
/* This is C99. */
#define __STDC_C99__
#elif (__STDC_VERSION__ >= 199409L)
/* This is C89 with amendment 1. */
#define __STDC_C89__
#define __STDC_C89_AMENDMENT_1__
#else
/* This is C89 without amendment 1. */
#define __STDC_C89__
#endif
#else /* !defined(__STDC_VERSION__) */
/* This is C89. __STDC_VERSION__ is not defined. */
#define __STDC_C89__
#endif
#else /* !defined(__STDC__) */
/* This is not standard C. __STDC__ is not defined. */
#endif
#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
/* Try to find out if we're compiling for WinRT or non-WinRT */
#if defined(_MSC_VER) && defined(__has_include)
#if __has_include(<winapifamily.h>)
#define HAVE_WINAPIFAMILY_H 1
#else
#define HAVE_WINAPIFAMILY_H 0
#endif
/* If _USING_V110_SDK71_ is defined it means we are using the Windows XP toolset. */
#elif defined(_MSC_VER) && (_MSC_VER >= 1700 && !_USING_V110_SDK71_) /* _MSC_VER == 1700 for Visual Studio 2012 */
#define HAVE_WINAPIFAMILY_H 1
#else
#define HAVE_WINAPIFAMILY_H 0
#endif
#if HAVE_WINAPIFAMILY_H
#include <winapifamily.h>
#define WINAPI_FAMILY_WINRT (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP))
#else
#define WINAPI_FAMILY_WINRT 0
#endif /* HAVE_WINAPIFAMILY_H */
#if WINAPI_FAMILY_WINRT
#undef __WINRT__
#define __WINRT__ 1
#endif
/* MSVC obviously has to have some non-standard constants... */
#if _M_IX86_FP == 1
#define __SSE__ 1
#elif _M_IX86_FP == 2 || (defined(_M_AMD64) || defined(_M_X64))
#define __SSE__ 1
#define __SSE2__ 1
#endif
#endif
#endif

View File

@ -159,14 +159,24 @@ typedef struct
# ifdef _WIN64
# define PRI_SIZET PRIu64
# else
#if _MSC_VER == 1800
# define PRI_SIZET PRIu32
#else
# define PRI_SIZET "u"
#endif
# if _MSC_VER == 1800
# define PRI_SIZET PRIu32
# else
# define PRI_SIZET "u"
# endif
# endif
#elif PS2
# define PRI_SIZET "u"
#else
# define PRI_SIZET "lu"
# if (SIZE_MAX == 0xFFFF)
# define PRI_SIZET "hu"
# elif (SIZE_MAX == 0xFFFFFFFF)
# define PRI_SIZET "u"
# elif (SIZE_MAX == 0xFFFFFFFFFFFFFFFF)
# define PRI_SIZET "lu"
# else
# error PRI_SIZET: unknown SIZE_MAX
# endif
#endif
#endif

View File

@ -36,6 +36,7 @@
#include <boolean.h>
#include <stdarg.h>
#include <vfs/vfs_implementation.h>
#define FILESTREAM_REQUIRED_VFS_VERSION 2
@ -60,7 +61,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length);
* Opens a file for reading or writing, depending on the requested mode.
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
**/
RFILE *filestream_open(const char *path, unsigned mode, unsigned hints);
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position);
@ -76,7 +77,7 @@ int filestream_close(RFILE *stream);
int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
char *filestream_gets(RFILE *stream, char *s, size_t len);
char* filestream_gets(RFILE *stream, char *s, size_t len);
int filestream_getc(RFILE *stream);
@ -100,11 +101,14 @@ int filestream_delete(const char *path);
int filestream_rename(const char *old_path, const char *new_path);
const char *filestream_get_path(RFILE *stream);
const char* filestream_get_path(RFILE *stream);
bool filestream_exists(const char *path);
char *filestream_getline(RFILE *stream);
/* Returned pointer must be freed by the caller. */
char* filestream_getline(RFILE *stream);
libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream);
RETRO_END_DECLS

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2018 The RetroArch team
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (stdstring.h).
@ -37,46 +37,20 @@ RETRO_BEGIN_DECLS
static INLINE bool string_is_empty(const char *data)
{
return (data == NULL) || (*data == '\0');
return !data || (*data == '\0');
}
static INLINE bool string_is_equal(const char *a, const char *b)
{
if (!a || !b)
return false;
while(*a && (*a == *b))
{
a++;
b++;
}
return (*(const unsigned char*)a - *(const unsigned char*)b) == 0;
return (a && b) ? !strcmp(a, b) : false;
}
static INLINE bool string_is_not_equal(const char *a, const char *b)
{
return !string_is_equal(a, b);
}
#define STRLEN_CONST(x) ((sizeof((x))-1))
#define string_add_pair_open(s, size) strlcat((s), " (", (size))
#define string_add_pair_close(s, size) strlcat((s), ")", (size))
#define string_add_bracket_open(s, size) strlcat((s), "{", (size))
#define string_add_bracket_close(s, size) strlcat((s), "}", (size))
#define string_add_single_quote(s, size) strlcat((s), "'", (size))
#define string_add_quote(s, size) strlcat((s), "\"", (size))
#define string_add_colon(s, size) strlcat((s), ":", (size))
#define string_add_glob_open(s, size) strlcat((s), "glob('*", (size))
#define string_add_glob_close(s, size) strlcat((s), "*')", (size))
static INLINE void string_add_between_pairs(char *s, const char *str,
size_t size)
{
string_add_pair_open(s, size);
strlcat(s, str, size);
string_add_pair_close(s, size);
}
#define string_is_not_equal(a, b) !string_is_equal((a), (b))
#define string_is_not_equal_fast(a, b, size) (memcmp(a, b, size) != 0)
#define string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0)
#define string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0)
static INLINE bool string_is_equal_case_insensitive(const char *a,
const char *b)
@ -119,7 +93,7 @@ char *string_to_upper(char *s);
char *string_to_lower(char *s);
char *string_ucwords(char* s);
char *string_ucwords(char *s);
char *string_replace_substring(const char *in, const char *pattern,
const char *by);
@ -133,7 +107,39 @@ char *string_trim_whitespace_right(char *const s);
/* Remove leading and trailing whitespaces */
char *string_trim_whitespace(char *const s);
char *word_wrap(char* buffer, const char *string, int line_width, bool unicode);
/* max_lines == 0 means no limit */
char *word_wrap(char *buffer, const char *string,
int line_width, bool unicode, unsigned max_lines);
/* Splits string into tokens seperated by 'delim'
* > Returned token string must be free()'d
* > Returns NULL if token is not found
* > After each call, 'str' is set to the position after the
* last found token
* > Tokens *include* empty strings
* Usage example:
* char *str = "1,2,3,4,5,6,7,,,10,";
* char **str_ptr = &str;
* char *token = NULL;
* while((token = string_tokenize(str_ptr, ",")))
* {
* printf("%s\n", token);
* free(token);
* token = NULL;
* }
*/
char* string_tokenize(char **str, const char *delim);
/* Removes every instance of character 'c' from 'str' */
void string_remove_all_chars(char *str, char c);
/* Replaces every instance of character 'find' in 'str'
* with character 'replace' */
void string_replace_all_chars(char *str, char find, char replace);
/* Converts string to unsigned integer.
* Returns 0 if string is invalid */
unsigned string_to_unsigned(const char *str);
RETRO_END_DECLS

View File

@ -0,0 +1,111 @@
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (vfs_implementation.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_VFS_H
#define __LIBRETRO_SDK_VFS_H
#include <retro_common_api.h>
#include <boolean.h>
#ifdef RARCH_INTERNAL
#ifndef VFS_FRONTEND
#define VFS_FRONTEND
#endif
#endif
RETRO_BEGIN_DECLS
#ifdef _WIN32
typedef void* HANDLE;
#endif
#ifdef HAVE_CDROM
typedef struct
{
char *cue_buf;
size_t cue_len;
int64_t byte_pos;
char drive;
unsigned char cur_min;
unsigned char cur_sec;
unsigned char cur_frame;
unsigned char cur_track;
unsigned cur_lba;
unsigned last_frame_lba;
unsigned char last_frame[2352];
bool last_frame_valid;
} vfs_cdrom_t;
#endif
enum vfs_scheme
{
VFS_SCHEME_NONE = 0,
VFS_SCHEME_CDROM
};
#ifndef __WINRT__
#ifdef VFS_FRONTEND
struct retro_vfs_file_handle
#else
struct libretro_vfs_implementation_file
#endif
{
int fd;
unsigned hints;
int64_t size;
char *buf;
FILE *fp;
#ifdef _WIN32
HANDLE fh;
#endif
char* orig_path;
uint64_t mappos;
uint64_t mapsize;
uint8_t *mapped;
enum vfs_scheme scheme;
#ifdef HAVE_CDROM
vfs_cdrom_t cdrom;
#endif
};
#endif
/* Replace the following symbol with something appropriate
* to signify the file is being compiled for a front end instead of a core.
* This allows the same code to act as reference implementation
* for VFS and as fallbacks for when the front end does not provide VFS functionality.
*/
#ifdef VFS_FRONTEND
typedef struct retro_vfs_file_handle libretro_vfs_implementation_file;
#else
typedef struct libretro_vfs_implementation_file libretro_vfs_implementation_file;
#endif
#ifdef VFS_FRONTEND
typedef struct retro_vfs_dir_handle libretro_vfs_implementation_dir;
#else
typedef struct libretro_vfs_implementation_dir libretro_vfs_implementation_dir;
#endif
RETRO_END_DECLS
#endif

View File

@ -23,30 +23,13 @@
#ifndef __LIBRETRO_SDK_VFS_IMPLEMENTATION_H
#define __LIBRETRO_SDK_VFS_IMPLEMENTATION_H
#include <stdio.h>
#include <stdint.h>
#include <libretro.h>
#include <retro_environment.h>
#include <vfs/vfs.h>
/* Replace the following symbol with something appropriate
* to signify the file is being compiled for a front end instead of a core.
* This allows the same code to act as reference implementation
* for VFS and as fallbacks for when the front end does not provide VFS functionality.
*/
#ifdef VFS_FRONTEND
typedef struct retro_vfs_file_handle libretro_vfs_implementation_file;
#else
typedef struct libretro_vfs_implementation_file libretro_vfs_implementation_file;
#endif
#ifdef VFS_FRONTEND
typedef struct retro_vfs_dir_handle libretro_vfs_implementation_dir;
#else
typedef struct libretro_vfs_implementation_dir libretro_vfs_implementation_dir;
#endif
#ifdef __cplusplus
extern "C" {
#endif
RETRO_BEGIN_DECLS
libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, unsigned mode, unsigned hints);
@ -88,8 +71,6 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *dirstream);
int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *dirstream);
#ifdef __cplusplus
}
#endif
RETRO_END_DECLS
#endif

View File

@ -154,7 +154,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length)
* Opens a file for reading or writing, depending on the requested mode.
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
**/
RFILE *filestream_open(const char *path, unsigned mode, unsigned hints)
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
{
struct retro_vfs_file_handle *fp = NULL;
RFILE* output = NULL;
@ -176,7 +176,7 @@ RFILE *filestream_open(const char *path, unsigned mode, unsigned hints)
return output;
}
char *filestream_gets(RFILE *stream, char *s, size_t len)
char* filestream_gets(RFILE *stream, char *s, size_t len)
{
int c = 0;
char *p = s;
@ -204,9 +204,9 @@ int filestream_getc(RFILE *stream)
{
char c = 0;
if (!stream)
return 0;
if(filestream_read(stream, &c, 1) == 1)
return (int)c;
return EOF;
if (filestream_read(stream, &c, 1) == 1)
return (int)(unsigned char)c;
return EOF;
}
@ -215,28 +215,40 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
char buf[4096];
char subfmt[64];
va_list args;
const char * bufiter = buf;
int64_t startpos = filestream_tell(stream);
int ret = 0;
int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
if (maxlen <= 0)
return EOF;
buf[maxlen] = '\0';
va_start(args, format);
while (*format)
{
if (*format == '%')
{
int sublen;
char* subfmtiter = subfmt;
bool asterisk = false;
*subfmtiter++ = *format++; /* '%' */
/* %[*][width][length]specifier */
if (*format == '*')
{
asterisk = true;
*subfmtiter++ = *format++;
}
while (isdigit(*format)) *subfmtiter++ = *format++; /* width */
/* length */
if (*format == 'h' || *format == 'l')
{
@ -247,6 +259,7 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
{
*subfmtiter++ = *format++;
}
/* specifier - always a single character (except ]) */
if (*format == '[')
{
@ -254,9 +267,11 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
*subfmtiter++ = *format++;
}
else *subfmtiter++ = *format++;
*subfmtiter++ = '%';
*subfmtiter++ = 'n';
*subfmtiter++ = '\0';
if (sizeof(void*) != sizeof(long*)) abort(); /* all pointers must have the same size */
if (asterisk)
{
@ -266,6 +281,7 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
{
if (sscanf(bufiter, subfmt, va_arg(args, void*), &sublen) != 1) break;
}
ret++;
bufiter += sublen;
}
@ -282,8 +298,10 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
format++;
}
}
va_end(args);
filestream_seek(stream, startpos+(bufiter-buf), RETRO_VFS_SEEK_POSITION_START);
return ret;
}
@ -381,7 +399,7 @@ int filestream_rename(const char *old_path, const char *new_path)
return retro_vfs_file_rename_impl(old_path, new_path);
}
const char *filestream_get_path(RFILE *stream)
const char* filestream_get_path(RFILE *stream)
{
if (filestream_get_path_cb != NULL)
return filestream_get_path_cb(stream->hfile);
@ -409,7 +427,7 @@ int filestream_putc(RFILE *stream, int c)
char c_char = (char)c;
if (!stream)
return EOF;
return filestream_write(stream, &c_char, 1)==1 ? c : EOF;
return filestream_write(stream, &c_char, 1)==1 ? (int)(unsigned char)c : EOF;
}
int filestream_vprintf(RFILE *stream, const char* format, va_list args)
@ -554,13 +572,14 @@ bool filestream_write_file(const char *path, const void *data, int64_t size)
return true;
}
char *filestream_getline(RFILE *stream)
/* Returned pointer must be freed by the caller. */
char* filestream_getline(RFILE *stream)
{
char* newline_tmp = NULL;
char *newline_tmp = NULL;
size_t cur_size = 8;
size_t idx = 0;
int in = 0;
char* newline = (char*)malloc(9);
char *newline = (char*)malloc(9);
if (!stream || !newline)
{
@ -594,3 +613,8 @@ char *filestream_getline(RFILE *stream)
newline[idx] = '\0';
return newline;
}
libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream)
{
return (libretro_vfs_implementation_file*)stream->hfile;
}

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,11 @@
#include "mednafen/mempatcher.h"
#include "mednafen/git.h"
#include "mednafen/general.h"
#include "mednafen/md5.h"
#include <libretro.h>
#include <streams/file_stream.h>
#include <algorithm>
#include "mednafen/lynx/system.h"
#include "libretro_core_options.h"
#ifdef _MSC_VER
#include <compat/msvc.h>
@ -29,7 +30,9 @@ static bool overscan;
static double last_sound_rate;
static MDFN_PixelFormat last_pixel_format;
static bool auto_rotate;
static unsigned rot_screen;
static unsigned rot_screen_last_frame;
static unsigned select_pressed_last_frame;
static MDFN_Surface *surf;
@ -44,6 +47,8 @@ std::string retro_base_directory;
std::string retro_base_name;
std::string retro_save_directory;
static bool libretro_supports_input_bitmasks;
static void set_basename(const char *path)
{
const char *base = strrchr(path, '/');
@ -58,495 +63,9 @@ static void set_basename(const char *path)
retro_base_name = retro_base_name.substr(0, retro_base_name.find_last_of('.'));
}
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// System object class //
//////////////////////////////////////////////////////////////////////////////
// //
// This class provides the glue to bind of of the emulation objects //
// together via peek/poke handlers and pass thru interfaces to lower //
// objects, all control of the emulator is done via this class. Update() //
// does most of the work and each call emulates one CPU instruction and //
// updates all of the relevant hardware if required. It must be remembered //
// that if that instruction involves setting SPRGO then, it will cause a //
// sprite painting operation and then a corresponding update of all of the //
// hardware which will usually involve recursive calls to Update, see //
// Mikey SPRGO code for more details. //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#define SYSTEM_CPP
//#include <crtdbg.h>
//#define TRACE_SYSTEM
#include "mednafen/lynx/system.h"
#include "mednafen/general.h"
#include "mednafen/mempatcher.h"
CSystem::CSystem(const uint8 *filememory, int32 filesize)
:mCart(NULL),
mRom(NULL),
mMemMap(NULL),
mRam(NULL),
mCpu(NULL),
mMikie(NULL),
mSusie(NULL)
{
mFileType=HANDY_FILETYPE_ILLEGAL;
if(filesize < 11)
{
/* Lynx ROM image is too short. */
}
char clip[11];
memcpy(clip,filememory,11);
clip[4]=0;
clip[10]=0;
if(!strcmp(&clip[6],"BS93")) mFileType=HANDY_FILETYPE_HOMEBREW;
else if(!strcmp(&clip[0],"LYNX")) mFileType=HANDY_FILETYPE_LNX;
else if(filesize==128*1024 || filesize==256*1024 || filesize==512*1024)
{
/* Invalid Cart (type). but 128/256/512k size -> set to RAW and try to load raw rom image */
mFileType=HANDY_FILETYPE_RAW;
}
else
{
/* File format is unknown to module. This will then
* just load the core into an "Insert Game" screen */
}
MDFNMP_Init(65536, 1);
// Create the system objects that we'll use
// Attempt to load the cartridge errors caught above here...
mRom = new CRom(MDFN_MakeFName(MDFNMKF_FIRMWARE, 0, "lynxboot.img").c_str());
// An exception from this will be caught by the level above
switch(mFileType)
{
case HANDY_FILETYPE_RAW:
case HANDY_FILETYPE_LNX:
mCart = new CCart(filememory,filesize);
mRam = new CRam(0,0);
break;
case HANDY_FILETYPE_HOMEBREW:
mCart = new CCart(NULL, 0);
mRam = new CRam(filememory,filesize);
break;
case HANDY_FILETYPE_SNAPSHOT:
case HANDY_FILETYPE_ILLEGAL:
default:
mCart = new CCart(0,0);
mRam = new CRam(0,0);
break;
}
// These can generate exceptions
mMikie = new CMikie(*this);
mSusie = new CSusie(*this);
// Instantiate the memory map handler
mMemMap = new CMemMap(*this);
// Now the handlers are set we can instantiate the CPU as is will use handlers on reset
mCpu = new C65C02(*this);
// Now init is complete do a reset, this will cause many things to be reset twice
// but what the hell, who cares, I don't.....
Reset();
}
CSystem::~CSystem()
{
// Cleanup all our objects
if(mCart!=NULL) delete mCart;
if(mRom!=NULL) delete mRom;
if(mRam!=NULL) delete mRam;
if(mCpu!=NULL) delete mCpu;
if(mMikie!=NULL) delete mMikie;
if(mSusie!=NULL) delete mSusie;
if(mMemMap!=NULL) delete mMemMap;
}
void CSystem::Reset(void)
{
gSystemCycleCount=0;
gNextTimerEvent=0;
gCPUBootAddress=0;
gSystemIRQ=false;
gSystemNMI=false;
gSystemCPUSleep=false;
gSystemHalt=false;
gSuzieDoneTime = 0;
mMemMap->Reset();
mCart->Reset();
mRom->Reset();
mRam->Reset();
mMikie->Reset();
mSusie->Reset();
mCpu->Reset();
// Homebrew hashup
if(mFileType==HANDY_FILETYPE_HOMEBREW)
{
mMikie->PresetForHomebrew();
C6502_REGS regs;
mCpu->GetRegs(regs);
regs.PC=(uint16)gCPUBootAddress;
mCpu->SetRegs(regs);
}
}
// Somewhat of a hack to make sure undrawn lines are black.
bool LynxLineDrawn[256];
static CSystem *lynxie = NULL;
extern MDFNGI EmulatedLynx;
static bool TestMagic(const char *name, MDFNFILE *fp)
{
uint8 data[64];
uint64 rc;
rc = fp->size;
if(rc >= CCart::HEADER_RAW_SIZE && CCart::TestMagic(data, sizeof(data)))
return true;
if(rc >= CRam::HEADER_RAW_SIZE && CRam::TestMagic(data, sizeof(data)))
return true;
return false;
}
static int Load(const uint8_t *data, size_t size)
{
lynxie = new CSystem(data, size);
switch(lynxie->CartGetRotate())
{
case CART_ROTATE_LEFT:
MDFNGameInfo->rotated = MDFN_ROTATE270;
break;
case CART_ROTATE_RIGHT:
MDFNGameInfo->rotated = MDFN_ROTATE90;
break;
}
if(lynxie->mRam->InfoRAMSize)
{
memcpy(MDFNGameInfo->MD5, lynxie->mRam->MD5, 16);
MDFN_printf(_("RAM: %u bytes\n"), lynxie->mRam->InfoRAMSize);
MDFN_printf(_("CRC32: 0x%08x\n"), lynxie->mRam->CRC32());
MDFN_printf(_("RAM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());
}
else
{
memcpy(MDFNGameInfo->MD5, lynxie->mCart->MD5, 16);
MDFN_printf(_("ROM: %dKiB\n"), (lynxie->mCart->InfoROMSize + 1023) / 1024);
MDFN_printf(_("CRC32: 0x%08x\n"), lynxie->mCart->CRC32());
MDFN_printf(_("ROM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());
}
MDFNGameInfo->fps = (uint32)(59.8 * 65536 * 256);
if(MDFN_GetSettingB("lynx.lowpass"))
{
lynxie->mMikie->miksynth.treble_eq(-35);
}
else
{
lynxie->mMikie->miksynth.treble_eq(0);
}
return(1);
}
static void CloseGame(void)
{
if(lynxie)
{
delete lynxie;
lynxie = NULL;
}
}
static uint8 *chee;
static void Emulate(EmulateSpecStruct *espec)
{
espec->DisplayRect.x = 0;
espec->DisplayRect.y = 0;
espec->DisplayRect.w = 160;
espec->DisplayRect.h = 102;
if(espec->VideoFormatChanged)
lynxie->DisplaySetAttributes(espec->surface->format); // FIXME, pitch
if(espec->SoundFormatChanged)
{
lynxie->mMikie->mikbuf.set_sample_rate(espec->SoundRate ? espec->SoundRate : 44100, 60);
lynxie->mMikie->mikbuf.clock_rate((long int)(16000000 / 4));
lynxie->mMikie->mikbuf.bass_freq(60);
lynxie->mMikie->miksynth.volume(0.50);
}
uint16 butt_data = chee[0] | (chee[1] << 8);
lynxie->SetButtonData(butt_data);
MDFNMP_ApplyPeriodicCheats();
memset(LynxLineDrawn, 0, sizeof(LynxLineDrawn[0]) * 102);
lynxie->mMikie->mpSkipFrame = espec->skip;
lynxie->mMikie->mpDisplayCurrent = espec->surface;
lynxie->mMikie->mpDisplayCurrentLine = 0;
lynxie->mMikie->startTS = gSystemCycleCount;
while(lynxie->mMikie->mpDisplayCurrent && (gSystemCycleCount - lynxie->mMikie->startTS) < 700000)
{
lynxie->Update();
// printf("%d ", gSystemCycleCount - lynxie->mMikie->startTS);
}
{
// FIXME, we should integrate this into mikie.*
uint32 color_black = espec->surface->MakeColor(30, 30, 30);
for(int y = 0; y < 102; y++)
{
if(espec->surface->format.bpp == 16)
{
uint16 *row = espec->surface->pixels16 + y * espec->surface->pitchinpix;
if(!LynxLineDrawn[y])
{
for(int x = 0; x < 160; x++)
row[x] = color_black;
}
}
else
{
uint32 *row = espec->surface->pixels + y * espec->surface->pitchinpix;
if(!LynxLineDrawn[y])
{
for(int x = 0; x < 160; x++)
row[x] = color_black;
}
}
}
}
espec->MasterCycles = gSystemCycleCount - lynxie->mMikie->startTS;
if(espec->SoundBuf)
{
lynxie->mMikie->mikbuf.end_frame((gSystemCycleCount - lynxie->mMikie->startTS) >> 2);
espec->SoundBufSize = lynxie->mMikie->mikbuf.read_samples(espec->SoundBuf, espec->SoundBufMaxSize) / 2; // divide by nr audio chn
}
else
espec->SoundBufSize = 0;
}
static void SetInput(int port, const char *type, void *ptr)
{
chee = (uint8 *)ptr;
}
static void TransformInput(void)
{
if(MDFN_GetSettingB("lynx.rotateinput"))
{
static const unsigned bp[4] = { 4, 6, 5, 7 };
const unsigned offs = MDFNGameInfo->rotated;
uint16 butt_data = MDFN_de16lsb(chee);
butt_data = (butt_data & 0xFF0F) |
(((butt_data >> bp[0]) & 1) << bp[(0 + offs) & 3]) |
(((butt_data >> bp[1]) & 1) << bp[(1 + offs) & 3]) |
(((butt_data >> bp[2]) & 1) << bp[(2 + offs) & 3]) |
(((butt_data >> bp[3]) & 1) << bp[(3 + offs) & 3]);
//printf("%d, %04x\n", MDFNGameInfo->rotated, butt_data);
MDFN_en16lsb(chee, butt_data);
}
}
int StateAction(StateMem *sm, int load, int data_only)
{
SFORMAT SystemRegs[] =
{
SFVAR(gSuzieDoneTime),
SFVAR(gSystemCycleCount),
SFVAR(gNextTimerEvent),
SFVAR(gCPUBootAddress),
SFVAR(gSystemIRQ),
SFVAR(gSystemNMI),
SFVAR(gSystemCPUSleep),
SFVAR(gSystemHalt),
SFARRAYN(lynxie->GetRamPointer(), RAM_SIZE, "RAM"),
SFEND
};
MDFNSS_StateAction(sm, load, data_only, SystemRegs, "SYST", false);
if(!lynxie->mSusie->StateAction(sm, load, data_only))
return(0);
if(!lynxie->mMemMap->StateAction(sm, load, data_only))
return(0);
if(!lynxie->mCart->StateAction(sm, load, data_only))
return(0);
if(!lynxie->mMikie->StateAction(sm, load, data_only))
return(0);
if(!lynxie->mCpu->StateAction(sm, load, data_only))
return(0);
return(1);
}
static void SetLayerEnableMask(uint64 mask)
{
}
static void DoSimpleCommand(int cmd)
{
switch(cmd)
{
case MDFN_MSC_POWER:
case MDFN_MSC_RESET: lynxie->Reset(); break;
}
}
static MDFNSetting LynxSettings[] =
{
{ "lynx.rotateinput", MDFNSF_NOFLAGS, "Virtually rotate D-pad along with screen.", NULL, MDFNST_BOOL, "1" },
{ "lynx.lowpass", MDFNSF_CAT_SOUND, "Enable sound output lowpass filter.", NULL, MDFNST_BOOL, "1" },
{ NULL }
};
static const InputDeviceInputInfoStruct IDII[] =
{
{ "a", "A (outer)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
{ "b", "B (inner)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
{ "option_2", "Option 2 (lower)", 5, IDIT_BUTTON_CAN_RAPID, NULL },
{ "option_1", "Option 1 (upper)", 4, IDIT_BUTTON_CAN_RAPID, NULL },
{ "left", "LEFT ←", /*VIRTB_DPAD0_L,*/ 2, IDIT_BUTTON, "right", { "up", "right", "down" } },
{ "right", "RIGHT →", /*VIRTB_DPAD0_R,*/ 3, IDIT_BUTTON, "left", { "down", "left", "up" } },
{ "up", "UP ↑", /*VIRTB_DPAD0_U,*/ 0, IDIT_BUTTON, "down", { "right", "down", "left" } },
{ "down", "DOWN ↓", /*VIRTB_DPAD0_D,*/ 1, IDIT_BUTTON, "up", { "left", "up", "right" } },
{ "pause", "PAUSE", 6, IDIT_BUTTON, NULL },
};
static InputDeviceInfoStruct InputDeviceInfo[] =
{
{
"gamepad",
"Gamepad",
NULL,
NULL,
sizeof(IDII) / sizeof(InputDeviceInputInfoStruct),
IDII,
}
};
static const InputPortInfoStruct PortInfo[] =
{
{ "builtin", "Built-In", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo, 0 }
};
static InputInfoStruct InputInfo =
{
sizeof(PortInfo) / sizeof(InputPortInfoStruct),
PortInfo
};
static const FileExtensionSpecStruct KnownExtensions[] =
{
{ ".lnx", "Atari Lynx ROM Image" },
{ NULL, NULL }
};
MDFNGI EmulatedLynx =
{
LynxSettings,
MDFN_MASTERCLOCK_FIXED(16000000),
0,
false, // Multires possible?
160, // lcm_width
102, // lcm_height
NULL, // Dummy
160, // Nominal width
102, // Nominal height
160, // Framebuffer width
102, // Framebuffer height
2, // Number of output sound channels
};
#define MEDNAFEN_CORE_NAME_MODULE "lynx"
#define MEDNAFEN_CORE_NAME "Beetle Lynx"
#define MEDNAFEN_CORE_VERSION "v0.9.47"
#define MEDNAFEN_CORE_VERSION "v1.24.0"
#define MEDNAFEN_CORE_EXTENSIONS "lnx"
#define MEDNAFEN_CORE_TIMING_FPS 75.0
#define MEDNAFEN_CORE_GEOMETRY_BASE_W 160
@ -572,7 +91,7 @@ void retro_init(void)
struct retro_log_callback log;
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
log_cb = log.log;
else
else
log_cb = NULL;
MDFNI_InitializeModule();
@ -598,7 +117,7 @@ void retro_init(void)
log_cb(RETRO_LOG_WARN, "System directory is not defined. Fallback on using same dir as ROM for system directory later ...\n");
failed_init = true;
}
if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir) && dir)
{
// If save directory is defined use it, otherwise use system directory
@ -609,7 +128,7 @@ void retro_init(void)
if (last != std::string::npos)
last++;
retro_save_directory = retro_save_directory.substr(0, last);
retro_save_directory = retro_save_directory.substr(0, last);
}
else
{
@ -617,7 +136,7 @@ void retro_init(void)
if (log_cb)
log_cb(RETRO_LOG_WARN, "Save directory is not defined. Fallback on using SYSTEM directory ...\n");
retro_save_directory = retro_base_directory;
}
}
#if defined(WANT_16BPP) && defined(FRONTEND_SUPPORTS_RGB565)
enum retro_pixel_format rgb565 = RETRO_PIXEL_FORMAT_RGB565;
@ -631,6 +150,11 @@ void retro_init(void)
perf_get_cpu_features_cb = NULL;
check_system_specs();
libretro_set_core_options(environ_cb);
if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
libretro_supports_input_bitmasks = true;
}
void retro_reset(void)
@ -656,13 +180,24 @@ static void set_volume (uint32_t *ptr, unsigned number)
static void check_variables(void)
{
struct retro_variable var = {0};
var.key = "lynx_rotate";
var.value = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
if (strcmp(var.value, "disabled") == 0)
auto_rotate = false;
else
auto_rotate = true;
rot_screen = 0;
}
}
#define MAX_PLAYERS 1
#define MAX_BUTTONS 9
static uint8_t input_buf[2];
static MDFNGI *MDFNI_LoadGame(const uint8_t *, size_t);
static MDFNGI *MDFNI_LoadGame(const uint8_t *data, size_t size);
bool retro_load_game(const struct retro_game_info *info)
{
if (!info || failed_init)
@ -704,13 +239,15 @@ bool retro_load_game(const struct retro_game_info *info)
MDFN_PixelFormat pix_fmt(MDFN_COLORSPACE_RGB, 16, 8, 0, 24);
memset(&last_pixel_format, 0, sizeof(MDFN_PixelFormat));
surf = new MDFN_Surface(NULL, FB_WIDTH, FB_HEIGHT, FB_WIDTH, pix_fmt);
SetInput(0, "gamepad", &input_buf);
SetInput(0, "gamepad", (uint8_t*)&input_buf);
auto_rotate = false;
rot_screen = 0;
select_pressed_last_frame = 0;
rot_screen_last_frame = 0;
check_variables();
@ -725,8 +262,6 @@ void retro_unload_game()
MDFNI_CloseGame();
}
// Hardcoded for PSX. No reason to parse lots of structures ...
// See mednafen/psx/input/gamepad.cpp
static void update_input(void)
{
static unsigned map[4][9] = {
@ -776,22 +311,45 @@ static void update_input(void)
},
};
unsigned select_button = 0;
uint16_t input_state = 0;
for (unsigned i = 0; i < MAX_BUTTONS; i++)
input_state |= input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, map[rot_screen][i]) ? (1 << i) : 0;
if (libretro_supports_input_bitmasks)
{
int16_t ret = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
for (unsigned i = 0; i < MAX_BUTTONS; i++)
input_state |= ret & (1 << map[rot_screen][i]) ? (1 << i) : 0;
select_button = ret & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT);
}
else
{
for (unsigned i = 0; i < MAX_BUTTONS; i++)
input_state |= input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, map[rot_screen][i]) ? (1 << i) : 0;
select_button = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT);
}
// Input data must be little endian.
input_buf[0] = (input_state >> 0) & 0xff;
input_buf[1] = (input_state >> 8) & 0xff;
unsigned select_button = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT);
if (select_button && !select_pressed_last_frame)
if (auto_rotate)
{
switch (lynxie->CartGetRotate())
{
case CART_ROTATE_RIGHT: rot_screen = 3; break;
case CART_ROTATE_LEFT: rot_screen = 1; break;
default: rot_screen = 0; break;
}
}
if (!auto_rotate && (select_button && !select_pressed_last_frame))
rot_screen++;
if (rot_screen != rot_screen_last_frame)
{
if (rot_screen > 3)
rot_screen = 0;
rot_screen_last_frame = rot_screen;
const float aspect[2] = { (80.0 / 51.0), (51.0 / 80.0) };
const unsigned rot_angle[4] = { 0, 1, 2, 3 };
@ -801,11 +359,9 @@ static void update_input(void)
}
select_pressed_last_frame = select_button;
rot_screen_last_frame = rot_screen;
}
static uint64_t video_frames, audio_frames;
void retro_run()
{
input_poll_cb();
@ -860,9 +416,6 @@ void retro_run()
video_cb(pix, width, height, FB_WIDTH << 1);
#endif
video_frames++;
audio_frames += spec.SoundBufSize;
audio_batch_cb(spec.SoundBuf, spec.SoundBufSize);
bool updated = false;
@ -900,13 +453,7 @@ void retro_deinit()
delete surf;
surf = NULL;
if (log_cb)
{
log_cb(RETRO_LOG_INFO, "[%s]: Samples / Frame: %.5f\n",
mednafen_core_str, (double)audio_frames / video_frames);
log_cb(RETRO_LOG_INFO, "[%s]: Estimated FPS: %.5f\n",
mednafen_core_str, (double)video_frames * 44100 / audio_frames);
}
libretro_supports_input_bitmasks = false;
}
unsigned retro_get_region(void)
@ -1067,7 +614,7 @@ std::string MDFN_MakeFName(MakeFName_Type type, int id1, const char *cd1)
sanitize_path(ret); // Because Windows path handling is mongoloid.
#endif
break;
default:
default:
break;
}
@ -1128,11 +675,17 @@ void MDFN_ResetMessages(void)
static MDFNGI *MDFNI_LoadGame(const uint8_t *data, size_t size)
{
if (!data || !size) {
MDFN_indent(2);
goto error;
MDFN_indent(-2);
MDFNGameInfo = NULL;
return NULL;
}
MDFNGameInfo = &EmulatedLynx;
MDFNFILE *GameFile = file_open_mem(data, size);
if (!GameFile)
return NULL;
MDFNGameInfo = &EmulatedLynx;
MDFN_indent(1);
@ -1146,8 +699,7 @@ static MDFNGI *MDFNI_LoadGame(const uint8_t *data, size_t size)
// End load per-game settings
//
if(Load(data, size) <= 0)
goto error;
Load(GameFile);
MDFN_LoadGameCheats(NULL);
MDFNMP_InstallReadPatches();
@ -1158,9 +710,6 @@ static MDFNGI *MDFNI_LoadGame(const uint8_t *data, size_t size)
return(MDFNGameInfo);
error:
MDFN_indent(-2);
MDFNGameInfo = NULL;
return NULL;
}

264
libretro_core_options.h Normal file
View File

@ -0,0 +1,264 @@
#ifndef LIBRETRO_CORE_OPTIONS_H__
#define LIBRETRO_CORE_OPTIONS_H__
#include <stdlib.h>
#include <string.h>
#include <libretro.h>
#include <retro_inline.h>
#ifndef HAVE_NO_LANGEXTRA
#include "libretro_core_options_intl.h"
#endif
/*
********************************
* VERSION: 1.3
********************************
*
* - 1.3: Move translations to libretro_core_options_intl.h
* - libretro_core_options_intl.h includes BOM and utf-8
* fix for MSVC 2010-2013
* - Added HAVE_NO_LANGEXTRA flag to disable translations
* on platforms/compilers without BOM support
* - 1.2: Use core options v1 interface when
* RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1
* (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)
* - 1.1: Support generation of core options v0 retro_core_option_value
* arrays containing options with a single value
* - 1.0: First commit
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
********************************
* Core Option Definitions
********************************
*/
/* RETRO_LANGUAGE_ENGLISH */
/* Default language:
* - All other languages must include the same keys and values
* - Will be used as a fallback in the event that frontend language
* is not available
* - Will be used as a fallback for any missing entries in
* frontend language definition */
struct retro_core_option_definition option_defs_us[] = {
/* These variable names and possible values constitute an ABI with ZMZ (ZSNES Libretro player).
* Changing "Show layer 1" is fine, but don't change "layer_1"/etc or the possible values ("Yes|No").
* Adding more variables and rearranging them is safe. */
{
"lynx_rotate",
"Auto-rotate screen and button mappings",
"Virtually rotate screen orientation and keymaps automatically for known games. When disabled, screen rotation is manually adjusted by pressing the SELECT button.",
{
{ "enabled", NULL },
{ "disabled", NULL },
{ NULL, NULL},
},
"enabled"
},
{ NULL, NULL, NULL, {{0}}, NULL },
};
/*
********************************
* Language Mapping
********************************
*/
#ifndef HAVE_NO_LANGEXTRA
struct retro_core_option_definition *option_defs_intl[RETRO_LANGUAGE_LAST] = {
option_defs_us, /* RETRO_LANGUAGE_ENGLISH */
NULL, /* RETRO_LANGUAGE_JAPANESE */
NULL, /* RETRO_LANGUAGE_FRENCH */
NULL, /* RETRO_LANGUAGE_SPANISH */
NULL, /* RETRO_LANGUAGE_GERMAN */
NULL, /* RETRO_LANGUAGE_ITALIAN */
NULL, /* RETRO_LANGUAGE_DUTCH */
NULL, /* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */
NULL, /* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */
NULL, /* RETRO_LANGUAGE_RUSSIAN */
NULL, /* RETRO_LANGUAGE_KOREAN */
NULL, /* RETRO_LANGUAGE_CHINESE_TRADITIONAL */
NULL, /* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */
NULL, /* RETRO_LANGUAGE_ESPERANTO */
NULL, /* RETRO_LANGUAGE_POLISH */
NULL, /* RETRO_LANGUAGE_VIETNAMESE */
NULL, /* RETRO_LANGUAGE_ARABIC */
NULL, /* RETRO_LANGUAGE_GREEK */
NULL, /* RETRO_LANGUAGE_TURKISH */
};
#endif
/*
********************************
* Functions
********************************
*/
/* Handles configuration/setting of core options.
* Should be called as early as possible - ideally inside
* retro_set_environment(), and no later than retro_load_game()
* > We place the function body in the header to avoid the
* necessity of adding more .c files (i.e. want this to
* be as painless as possible for core devs)
*/
static INLINE void libretro_set_core_options(retro_environment_t environ_cb)
{
unsigned version = 0;
if (!environ_cb)
return;
if (environ_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version) && (version >= 1))
{
#ifndef HAVE_NO_LANGEXTRA
struct retro_core_options_intl core_options_intl;
unsigned language = 0;
core_options_intl.us = option_defs_us;
core_options_intl.local = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) &&
(language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH))
core_options_intl.local = option_defs_intl[language];
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_intl);
#else
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS, &option_defs_us);
#endif
}
else
{
size_t i;
size_t num_options = 0;
struct retro_variable *variables = NULL;
char **values_buf = NULL;
/* Determine number of options */
while (true)
{
if (option_defs_us[num_options].key)
num_options++;
else
break;
}
/* Allocate arrays */
variables = (struct retro_variable *)calloc(num_options + 1, sizeof(struct retro_variable));
values_buf = (char **)calloc(num_options, sizeof(char *));
if (!variables || !values_buf)
goto error;
/* Copy parameters from option_defs_us array */
for (i = 0; i < num_options; i++)
{
const char *key = option_defs_us[i].key;
const char *desc = option_defs_us[i].desc;
const char *default_value = option_defs_us[i].default_value;
struct retro_core_option_value *values = option_defs_us[i].values;
size_t buf_len = 3;
size_t default_index = 0;
values_buf[i] = NULL;
if (desc)
{
size_t num_values = 0;
/* Determine number of values */
while (true)
{
if (values[num_values].value)
{
/* Check if this is the default value */
if (default_value)
if (strcmp(values[num_values].value, default_value) == 0)
default_index = num_values;
buf_len += strlen(values[num_values].value);
num_values++;
}
else
break;
}
/* Build values string */
if (num_values > 0)
{
size_t j;
buf_len += num_values - 1;
buf_len += strlen(desc);
values_buf[i] = (char *)calloc(buf_len, sizeof(char));
if (!values_buf[i])
goto error;
strcpy(values_buf[i], desc);
strcat(values_buf[i], "; ");
/* Default value goes first */
strcat(values_buf[i], values[default_index].value);
/* Add remaining values */
for (j = 0; j < num_values; j++)
{
if (j != default_index)
{
strcat(values_buf[i], "|");
strcat(values_buf[i], values[j].value);
}
}
}
}
variables[i].key = key;
variables[i].value = values_buf[i];
}
/* Set variables */
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables);
error:
/* Clean up */
if (values_buf)
{
for (i = 0; i < num_options; i++)
{
if (values_buf[i])
{
free(values_buf[i]);
values_buf[i] = NULL;
}
}
free(values_buf);
values_buf = NULL;
}
if (variables)
{
free(variables);
variables = NULL;
}
}
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,80 @@
#ifndef LIBRETRO_CORE_OPTIONS_INTL_H__
#define LIBRETRO_CORE_OPTIONS_INTL_H__
#if defined(_MSC_VER) && (_MSC_VER >= 1500 && _MSC_VER < 1900)
/* https://support.microsoft.com/en-us/kb/980263 */
#pragma execution_character_set("utf-8")
#pragma warning(disable:4566)
#endif
#include <libretro.h>
/*
********************************
* VERSION: 1.3
********************************
*
* - 1.3: Move translations to libretro_core_options_intl.h
* - libretro_core_options_intl.h includes BOM and utf-8
* fix for MSVC 2010-2013
* - Added HAVE_NO_LANGEXTRA flag to disable translations
* on platforms/compilers without BOM support
* - 1.2: Use core options v1 interface when
* RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1
* (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)
* - 1.1: Support generation of core options v0 retro_core_option_value
* arrays containing options with a single value
* - 1.0: First commit
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
********************************
* Core Option Definitions
********************************
*/
/* RETRO_LANGUAGE_JAPANESE */
/* RETRO_LANGUAGE_FRENCH */
/* RETRO_LANGUAGE_SPANISH */
/* RETRO_LANGUAGE_GERMAN */
/* RETRO_LANGUAGE_ITALIAN */
/* RETRO_LANGUAGE_DUTCH */
/* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */
/* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */
/* RETRO_LANGUAGE_RUSSIAN */
/* RETRO_LANGUAGE_KOREAN */
/* RETRO_LANGUAGE_CHINESE_TRADITIONAL */
/* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */
/* RETRO_LANGUAGE_ESPERANTO */
/* RETRO_LANGUAGE_POLISH */
/* RETRO_LANGUAGE_VIETNAMESE */
/* RETRO_LANGUAGE_ARABIC */
/* RETRO_LANGUAGE_GREEK */
/* RETRO_LANGUAGE_TURKISH */
#ifdef __cplusplus
}
#endif
#endif

View File

@ -25,6 +25,24 @@
#include "file.h"
struct MDFNFILE *file_open_mem(const uint8_t *data, int64_t size)
{
struct MDFNFILE *file = (struct MDFNFILE*)calloc(1, sizeof(*file));
if (!file)
return NULL;
if (!data || !size)
return NULL;
file->data = (uint8_t*)data;
file->size = size;
file->ext = 0;
file->location = 0;
return file;
}
struct MDFNFILE *file_open(const char *path)
{
int64_t size = 0;
@ -66,3 +84,50 @@ int file_close(struct MDFNFILE *file)
return 1;
}
uint64_t file_read(struct MDFNFILE *file, void *ptr,
size_t element_size, size_t nmemb)
{
uint32_t total = element_size * nmemb;
if (file->location >= file->size)
return 0;
if ((file->location + total) > file->size)
{
int64_t ak = file->size - file->location;
memcpy((uint8_t*)ptr, file->data + file->location, ak);
file->location = file->size;
return(ak / element_size);
}
memcpy((uint8_t*)ptr, file->data + file->location, total);
file->location += total;
return nmemb;
}
int file_seek(struct MDFNFILE *file, int64_t offset, int whence)
{
switch(whence)
{
case SEEK_SET:
if (offset >= file->size)
return -1;
file->location = offset;
break;
case SEEK_CUR:
if ((offset + file->location) > file->size)
return -1;
file->location += offset;
break;
}
return 0;
}

View File

@ -15,10 +15,16 @@ struct MDFNFILE
int64_t location;
};
struct MDFNFILE *file_open_mem(const uint8_t *data, int64_t size);
struct MDFNFILE *file_open(const char *path);
int file_close(struct MDFNFILE *file);
uint64_t file_read(struct MDFNFILE *file, void *ptr,
size_t element_size, size_t nmemb);
int file_seek(struct MDFNFILE *file, int64_t offset, int whence);
#ifdef __cplusplus
}
#endif

View File

@ -50,12 +50,119 @@
#include <string.h>
#include "cart.h"
#include "../state.h"
#include <../md5.h>
#include "../md5.h"
#include "../../scrc32.h"
#include "../mednafen-endian.h"
static inline uint16_t MDFN_de16lsb(const uint8_t *morp)
static LYNX_DB lynxDB[] = {
{ 0x540e9bb7, "Alien vs Predator (USA) (Proto) (1993-12-17)", 262144, 0, 0 },
{ 0xf6fb48fb, "A.P.B. (USA, Europe)", 262144, 0, 0 },
{ 0x0483cd2a, "Awesome Golf (USA, Europe)", 262144, 0, 0 },
{ 0x3943c116, "Baseball Heroes (USA, Europe)", 262144, 0, 0 },
{ 0x4161bb4a, "Basketbrawl (USA, Europe)", 131072, 0, 0 },
{ 0x277f82c2, "Batman Returns (USA, Europe)", 262144, 0, 0 },
{ 0x779faece, "Battle Wheels (USA, Europe)", 131072, 0, 0 },
{ 0x30fee726, "Battlezone 2000 (USA, Europe)", 262144, 0, 0 },
{ 0x143a313e, "Bill & Ted's Excellent Adventure (USA, Europe)", 262144, 0, 0 },
{ 0x0d973c9d, "[BIOS] Atari Lynx (USA, Europe)", 512, 0, 0 },
{ 0x3cd75df3, "Block Out (USA, Europe)", 131072, 0, 0 },
{ 0xdaf587b1, "Blue Lightning (USA, Europe)", 131072, 0, 0 },
{ 0xbfe36525, "Blue Lightning (USA, Europe) (Demo)", 131072, 0, 0 },
{ 0x333daece, "Bubble Trouble (USA, Europe)", 262144, 0, 0 },
{ 0xa08f0b59, "California Games (USA, Europe)", 131072, 0, 0 },
{ 0x97501709, "Centipede (USA) (Proto)", 131072, CART_ROTATE_LEFT, 0 },
{ 0x19c5a7a5, "Checkered Flag (USA, Europe)", 262144, 0, 0 },
{ 0x6a5f53ed, "Chip's Challenge (USA, Europe)", 131072, 0, 0 },
{ 0xaec474c8, "Crystal Mines II (USA, Europe)", 131072, 0, 0 },
{ 0x99729395, "Daemonsgate (USA) (Proto)", 262144, 0, 0 },
{ 0xb9ac1fe5, "Desert Strike - Return to the Gulf (USA, Europe)", 262144, 0, 0 },
{ 0x50386cfa, "Dinolympics (USA, Europe)", 262144, 0, 0 },
{ 0xd565fbb7, "Dirty Larry - Renegade Cop (USA, Europe)", 262144, 0, 0 },
{ 0xfbfc0f05, "Double Dragon (USA, Europe)", 262144, 0, 0 },
{ 0x33bb74c7, "Dracula the Undead (USA, Europe)", 262144, 0, 0 },
{ 0xbd97116b, "Electrocop (USA, Europe)", 131072, 0, 0 },
{ 0xf83397f9, "European Soccer Challenge (USA, Europe)", 131072, 0, 0 },
{ 0x6bceaa9c, "Eye of the Beholder (USA) (Proto)", 131072, 0, 0 },
{ 0x9034ee27, "Fat Bobby (USA, Europe)", 262144, 0, 0 },
{ 0x7e4b5945, "Fidelity Ultimate Chess Challenge, The (USA, Europe)", 131072, 0, 0 },
{ 0x494cc568, "Gates of Zendocon (USA, Europe)", 131072, 0, 0 },
{ 0xac564baa, "Gauntlet - The Third Encounter (1990) [o1]", 262144, CART_ROTATE_RIGHT, 0 },
{ 0x7f0ec7ad, "Gauntlet - The Third Encounter (USA, Europe)", 131072, CART_ROTATE_RIGHT, 0 },
{ 0xd20a85fc, "Gordo 106 (USA, Europe)", 262144, 0, 0 },
{ 0x6df63834, "Hard Drivin' (USA, Europe)", 131072, 0, 0 },
{ 0xe8b45707, "Hockey (USA, Europe)", 262144, 0, 0 },
{ 0xe3041c6c, "Hydra (USA, Europe)", 262144, 0, 0 },
{ 0x5cf8bbf0, "Ishido - The Way of Stones (USA, Europe)", 131072, 0, 0 },
{ 0x2455b6cf, "Jimmy Connors' Tennis (USA, Europe)", 524288, 0, 0 },
{ 0x5dba792a, "Joust (USA, Europe)", 131072, 0, 0 },
{ 0xa53649f1, "Klax (USA, Europe)", 262144, CART_ROTATE_RIGHT, 0 },
{ 0xbed5ba2b, "Krazy Ace - Miniature Golf (USA, Europe)", 262144, 0, 0 },
{ 0xcd1bd405, "Kung Food (USA, Europe)", 262144, 0, 0 },
{ 0x39b9b8cc, "Lemmings (USA, Europe)", 262144, 0, 0 },
{ 0x0271b6e9, "Lexis (USA)", 262144, CART_ROTATE_LEFT, 0 },
{ 0xb1c25ef1, "Loopz (USA) (Proto)", 262144, 0, 0 },
{ 0x1091a268, "Lynx Casino (USA, Europe)", 262144, 0, 0 },
{ 0x28ada019, "Lynx II Production Test Program (USA) (v0.02) (Proto)", 262144, 0, 0 },
{ 0xaba6da3d, "Malibu Bikini Volleyball (USA, Europe)", 262144, 0, 0 },
{ 0xc3fa0d4d, "Marlboro Go! (Europe) (Proto)", 262144, 0, 0 },
{ 0x7de3783a, "Ms. Pac-Man (USA, Europe)", 131072, 0, 0 },
{ 0x006fd398, "NFL Football (USA, Europe)", 262144, CART_ROTATE_LEFT, 0 },
{ 0xf3e3f811, "Ninja Gaiden III - The Ancient Ship of Doom (USA, Europe)", 524288, 0, 0 },
{ 0x22d47d51, "Ninja Gaiden (USA, Europe)", 262144, 0, 0 },
{ 0xaa50dd22, "Pac-Land (USA, Europe)", 131072, 0, 0 },
{ 0x4cdfbd57, "Paperboy (USA, Europe)", 131072, 0, 0 },
{ 0x14d38ca7, "Pinball Jam (USA, Europe)", 262144, 0, 0 },
{ 0x2393135f, "Pit-Fighter (USA, Europe)", 524288, 0, 0 },
{ 0x99c42034, "Power Factor (USA, Europe)", 262144, 0, 0 },
{ 0xb9881423, "QIX (USA, Europe)", 131072, 0, 0 },
{ 0xbcd10c3a, "Raiden (USA) (v3.0) (Beta)", 262144, CART_ROTATE_LEFT, 0 },
{ 0xb10b7c8e, "Rampage (USA, Europe)", 262144, 0, 0 },
{ 0x139f301d, "Rampart (USA, Europe)", 262144, 0, 0 },
{ 0x6867e80c, "RoadBlasters (USA, Europe)", 262144, 0, 0 },
{ 0x69959a3b, "Road Riot 4WD (USA) (Proto 3)", 262144, 0, 0 },
{ 0xd1dff2b2, "Robo-Squash (USA, Europe)", 131072, 0, 0 },
{ 0x7a6049b5, "Robotron 2084 (USA, Europe)", 131072, 0, 0 },
{ 0x67e5bdba, "Rygar (USA, Europe)", 262144, 0, 0 },
{ 0xbe166f3b, "Scrapyard Dog (USA, Europe)", 262144, 0, 0 },
{ 0xeb78baa3, "Shadow of the Beast (USA, Europe)", 262144, 0, 0 },
{ 0x192bcd04, "Shanghai (USA, Europe)", 131072, 0, 0 },
{ 0x5b2308ed, "Steel Talons (USA, Europe)", 262144, 0, 0 },
{ 0x8595c40b, "S.T.U.N. Runner (USA, Europe)", 262144, 0, 0 },
{ 0x2da7e2a8, "Super Asteroids, Missile Command (USA, Europe)", 131072, 0, 0 },
{ 0x690caeb0, "Super Off-Road (USA, Europe)", 262144, 0, 0 },
{ 0xdfa61571, "Super Skweek (USA, Europe)", 262144, 0, 0 },
{ 0x13657705, "Switchblade II (USA, Europe)", 262144, 0, 0 },
{ 0xae267e29, "Todd's Adventures in Slime World (USA, Europe)", 131072, 0, 0 },
{ 0x156a4a4c, "Toki (USA, Europe)", 262144, 0, 0 },
{ 0x0590a9e3, "Tournament Cyberball (USA, Europe)", 262144, 0, 0 },
{ 0xa4b924d6, "Turbo Sub (USA, Europe)", 131072, 0, 0 },
{ 0x8d56828b, "Viking Child (USA, Europe)", 262144, 0, 0 },
{ 0xb946ba49, "Warbirds (USA, Europe)", 131072, 0, 0 },
{ 0x91233794, "World Class Soccer (USA, Europe)", 262144, 0, 0 },
{ 0x9bed736d, "Xenophobe (USA, Europe)", 131072, 0, 0 },
{ 0x89e2a595, "Xybots (USA, Europe)", 262144, 0, 0 },
{ 0xcb27199d, "Zarlor Mercenary (USA, Europe)", 131072, 0, 0 },
{ 0, NULL, 0, 0, 0 },
};
LYNX_DB CCart::CheckHash(const uint32 crc32)
{
return(morp[0] | (morp[1] << 8));
LYNX_DB ret = {};
unsigned i = 0;
found = false;
while (lynxDB[i].crc32 != 0)
{
if (lynxDB[i].crc32 == crc32)
{
found = true;
ret = lynxDB[i];
break;
}
i++;
}
return ret;
}
LYNX_HEADER CCart::DecodeHeader(const uint8 *data)
@ -100,39 +207,40 @@ bool CCart::TestMagic(const uint8 *data, uint32 size)
return(true);
}
CCart::CCart(const uint8 *gamedata, uint32 gamesize)
CCart::CCart(MDFNFILE *fp)
{
LYNX_HEADER header;
uint64 gamesize;
uint8 raw_header[HEADER_RAW_SIZE];
LYNX_HEADER header;
uint32 header_size = HEADER_RAW_SIZE;;
uint32 loop;
md5_context md5;
md5.starts();
mWriteEnableBank0=false;
mWriteEnableBank1=false;
mCartRAM=false;
if (gamesize) {
if(fp)
{
gamesize = GET_FSIZE_PTR(fp);
// Checkout the header bytes
memcpy(&raw_header, gamedata, sizeof(LYNX_HEADER));
file_read(fp, raw_header, sizeof(LYNX_HEADER), 1);
header = DecodeHeader(raw_header);
// Sanity checks on the header
if(header.magic[0]!= 'L' || header.magic[1]!='Y' || header.magic[2]!='N' || header.magic[3]!='X' || header.version!=1)
{
MDFN_printf("Invalid cart, no header?\n");
MDFN_printf("Trying to guess ROM layout\n");
memset(&header, 0, sizeof(LYNX_HEADER));
header_size = 0;
file_seek(fp, 0, SEEK_SET);
memset(&header, 0, HEADER_RAW_SIZE);
strncpy((char*)&header.cartname, "NO HEADER", 32);
strncpy((char*)&header.manufname, "HANDY", 16);
header.page_size_bank0 = gamesize >> 8; // Hard workaround...
}
else
else
{
gamedata += HEADER_RAW_SIZE;
gamesize -= HEADER_RAW_SIZE;
MDFN_printf("Found LYNX header!\n");
}
}
else
@ -145,13 +253,29 @@ CCart::CCart(const uint8 *gamedata, uint32 gamesize)
// Setup rotation
header.rotation = 0;
gamesize = HEADER_RAW_SIZE;
}
InfoROMSize = gamesize;
// Setup name & manufacturer
strncpy(mName,(char*)&header.cartname, 32);
strncpy(mManufacturer,(char*)&header.manufname, 16);
// Calculate checksum and check database for rom entries
mCRC32 = crc32(0, GET_FDATA_PTR(fp) + header_size, gamesize);
LYNX_DB db = CheckHash(mCRC32);
if (found)
{
MDFN_printf("Found lynx rom in database.\n");
MDFN_printf("Title: %s.\n", db.name);
header.page_size_bank0 = db.filesize >> 8;
header.rotation = db.rotation;
}
// Setup name & manufacturer
strncpy(mName, (char *)&header.cartname, 32);
strncpy(mManufacturer, (char *)&header.manufname, 16);
MDFN_printf("Cart Name: %s\n", mName);
MDFN_printf("Manufacturer: %s\n", mManufacturer);
// Setup rotation
mRotation=header.rotation;
@ -161,7 +285,7 @@ CCart::CCart(const uint8 *gamedata, uint32 gamesize)
CTYPE banktype0,banktype1;
switch(header.page_size_bank0)
switch(header.page_size_bank0)
{
case 0x000:
banktype0=UNUSED;
@ -233,7 +357,7 @@ CCart::CCart(const uint8 *gamedata, uint32 gamesize)
break;
}
// Make some space for the new carts
// Make some space for the new carts
mCartBank0 = new uint8[mMaskBank0+1];
mCartBank1 = new uint8[mMaskBank1+1];
@ -244,28 +368,31 @@ CCart::CCart(const uint8 *gamedata, uint32 gamesize)
// Initialiase
int bank0size = std::min((int)gamesize, (int)(mMaskBank0 + 1));
int bank1size = std::min((int)gamesize, (int)(mMaskBank1 + 1));
for (loop = 0; loop < bank0size; loop++)
for(loop=0;loop<mMaskBank0+1;loop++)
mCartBank0[loop] = DEFAULT_CART_CONTENTS;
for (loop = 0; loop < bank1size; loop++)
for(loop=0;loop<mMaskBank1+1;loop++)
mCartBank1[loop] = DEFAULT_CART_CONTENTS;
mCRC32 = 0;
// Read in the BANK0 bytes
if (mMaskBank0) {
memcpy(mCartBank0, gamedata, bank0size);
mCRC32 = crc32(0, mCartBank0, bank0size);
md5.update(mCartBank0, bank0size);
md5_context md5;
md5.starts();
if (mMaskBank0)
{
uint64 size = std::min<uint64>(gamesize, mMaskBank0 + 1);
file_read(fp, mCartBank0, size, 1);
md5.update(mCartBank0, size);
gamesize -= size;
}
// Read in the BANK1 bytes
if (mMaskBank1) {
memcpy(mCartBank1, gamedata + bank0size, bank1size);
mCRC32 = crc32(mCRC32, mCartBank1, bank1size);
md5.update(mCartBank1, bank1size);
if (mMaskBank1)
{
uint64 size = std::min<uint64>(gamesize, mMaskBank0 + 1);
file_read(fp, mCartBank1, size, 1);
md5.update(mCartBank1, size);
}
md5.finish(MD5);
@ -422,17 +549,17 @@ int CCart::StateAction(StateMem *sm, int load, int data_only)
{
SFORMAT CartRegs[] =
{
SFVAR(mCounter),
SFVAR(mShifter),
SFVAR(mAddrData),
SFVAR(mStrobe),
SFVAR(mShiftCount0),
SFVAR(mCountMask0),
SFVAR(mShiftCount1),
SFVAR(mCountMask1),
SFVAR(mBank),
SFVAR(mWriteEnableBank0),
SFVAR(mWriteEnableBank1),
SFVAR(mCounter),
SFVAR(mShifter),
SFVAR(mAddrData),
SFVAR(mStrobe),
SFVAR(mShiftCount0),
SFVAR(mCountMask0),
SFVAR(mShiftCount1),
SFVAR(mCountMask1),
SFVAR(mBank),
SFVAR(mWriteEnableBank0),
SFVAR(mWriteEnableBank1),
SFVAR(last_strobe),
SFARRAYN(mCartBank1, mCartRAM ? mMaskBank1 + 1 : 0, "mCartBank1"),
SFEND

View File

@ -65,13 +65,22 @@ struct LYNX_HEADER
uint8 spare[5];
};
struct LYNX_DB
{
uint32 crc32;
char name[100];
uint32 filesize;
uint32 rotation;
uint32 reserved;
};
class CCart : public CLynxBase
{
// Function members
public:
CCart(const uint8 *gamedata, uint32 gamesize) MDFN_COLD;
CCart(MDFNFILE *fp) MDFN_COLD;
~CCart() MDFN_COLD;
public:
@ -113,8 +122,8 @@ class CCart : public CLynxBase
uint32 mWriteEnableBank1;
uint32 mCartRAM;
uint8 MD5[16];
uint32 InfoROMSize;
uint8 MD5[16];
private:
EMMODE mBank;
uint32 mMaskBank0;
@ -136,7 +145,10 @@ class CCart : public CLynxBase
uint32 mCountMask1;
uint32 mCRC32;
int8 last_strobe;
int8 last_strobe;
bool found;
LYNX_DB CheckHash(const uint32 crc32);
};
#endif

View File

@ -48,6 +48,24 @@
#include "../mednafen.h"
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
uint8 High;
uint8 Low;
#else
uint8 Low;
uint8 High;
#endif
} Union8;
uint16 Val16;
};
} Uuint16;
// Read/Write Cycle definitions
#define CPU_RDWR_CYC 5
#define DMA_RDWR_CYC 4

View File

@ -390,46 +390,18 @@ void CMikie::DisplaySetAttributes(const MDFN_PixelFormat &format)
for(Spot.Index=0;Spot.Index<4096;Spot.Index++)
{
mColourMap[Spot.Index]= format.MakeColor(((Spot.Colours.Red * 15) + 30), ((Spot.Colours.Green * 15) + 30),
((Spot.Colours.Blue * 15) + 30));
uint8 r = ((Spot.Colours.Red * 15) + 30);
uint8 g = ((Spot.Colours.Green * 15) + 30);
uint8 b = ((Spot.Colours.Blue * 15) + 30);
mColourMap[Spot.Index]= format.MakeColor(r, g, b);
}
}
void CMikie::CopyLineSurface32(void)
template<typename T>
void CMikie::CopyLineSurface(void)
{
uint32_t* bitmap_tmp = mpDisplayCurrent->pixels + mpDisplayCurrentLine * mpDisplayCurrent->pitchinpix;
if(mpDisplayCurrentLine > 102)
{
printf("Lynx Line Overflow: %d\n", mpDisplayCurrentLine);
return;
}
for(uint32 loop = 0; loop < SCREEN_WIDTH / 2; loop++)
{
uint32 source = mpRamPointer[(uint16)mLynxAddr];
if(mDISPCTL_Flip)
{
mLynxAddr--;
*bitmap_tmp=mColourMap[mPalette[source&0x0f].Index];
bitmap_tmp++;
*bitmap_tmp=mColourMap[mPalette[source>>4].Index];
bitmap_tmp++;
}
else
{
mLynxAddr++;
*bitmap_tmp = mColourMap[mPalette[source>>4].Index];
bitmap_tmp++;
*bitmap_tmp = mColourMap[mPalette[source&0x0f].Index];
bitmap_tmp++;
}
}
}
void CMikie::CopyLineSurface16(void)
{
uint16_t* bitmap_tmp = mpDisplayCurrent->pixels16 + mpDisplayCurrentLine * mpDisplayCurrent->pitchinpix;
T* bitmap_tmp = mpDisplayCurrent->pix<T>() + mpDisplayCurrentLine * mpDisplayCurrent->pitchinpix;
if(mpDisplayCurrentLine > 102)
{
@ -475,7 +447,7 @@ uint32 CMikie::DisplayRenderLine(void)
}
// Logic says it should be 101 but testing on an actual lynx shows the rest
// persiod is between lines 102,101,100 with the new line being latched at
// period is between lines 102,101,100 with the new line being latched at
// the beginning of count==99 hence the code below !!
// Emulate REST signal
@ -517,11 +489,11 @@ uint32 CMikie::DisplayRenderLine(void)
switch(mpDisplayCurrent->format.bpp)
{
case 16:
CopyLineSurface16();
CopyLineSurface<uint16>();
break;
case 32:
CopyLineSurface32();
CopyLineSurface<uint32>();
break;
}
@ -928,7 +900,7 @@ void CMikie::Poke(uint32 addr,uint8 data)
{
C6502_REGS regs;
mSystem.GetRegs(regs);
MDFN_printf("Runtime Alert - System Halted\nCMikie::Poke(SYSCTL1) - Lynx power down occured at PC=$%04x.\nResetting system.\n",regs.PC);
MDFN_printf("Runtime Alert - System Halted\nCMikie::Poke(SYSCTL1) - Lynx power down occurred at PC=$%04x.\nResetting system.\n",regs.PC);
mSystem.Reset();
gSystemHalt=true;
}

View File

@ -395,8 +395,7 @@ class CMikie : public CLynxBase
uint32 mLynxLineDMACounter;
uint32 mLynxAddr;
void CopyLineSurface16(void);
void CopyLineSurface32(void);
template<typename T> void CopyLineSurface(void);
};

View File

@ -67,16 +67,18 @@ bool CRam::TestMagic(const uint8* data, uint64 test_size)
return true;
}
CRam::CRam(const uint8 *filememory,uint32 filesize)
CRam::CRam(MDFNFILE *fp)
:mRamXORData(NULL)
{
mFileSize=filesize;
InfoRAMSize = 0;
if(filesize)
if(fp)
{
uint8 raw_header[HEADER_RAW_SIZE];
md5_context md5;
md5.starts();
mCRC32 = 0;
memcpy(&raw_header, filememory, sizeof(raw_header));
file_read(fp, raw_header, sizeof(raw_header), 1);
file_seek(fp, 0, SEEK_SET);
if(memcmp(&raw_header[6], "BS93", 4))
{
@ -93,14 +95,10 @@ CRam::CRam(const uint8 *filememory,uint32 filesize)
//printf("load_addr=%04x, size=%04x, rc0=%04x, rc1=%04x\n", load_address, size, rc0, rc1);
md5_context md5;
md5.starts();
mCRC32 = 0;
memcpy(&mRamXORData[load_address], filememory, rc0);
file_read(fp, &mRamXORData[load_address], rc0, 1);
md5.update(&mRamXORData[load_address], rc0);
mCRC32 = crc32(mCRC32, &mRamXORData[load_address], rc0);
memcpy(&mRamXORData[0x0000], filememory + rc0, rc1);
file_read(fp, &mRamXORData[0x0000], rc1, 1);
md5.update(&mRamXORData[0x0000], rc1);
mCRC32 = crc32(mCRC32, &mRamXORData[0x0000], rc1);
@ -112,6 +110,8 @@ CRam::CRam(const uint8 *filememory,uint32 filesize)
boot_addr = load_address;
}
else
InfoRAMSize = 0;
// Reset will cause the loadup
Reset();
@ -119,20 +119,18 @@ CRam::CRam(const uint8 *filememory,uint32 filesize)
CRam::~CRam()
{
if(mFileSize)
{
delete[] mRamXORData;
}
if (mRamXORData != NULL)
delete[] mRamXORData;
}
void CRam::Reset(void)
{
MDFNMP_AddRAM(65536, 0x0000, mRamData);
for(int loop=0;loop<RAM_SIZE;loop++)
mRamData[loop]=DEFAULT_RAM_CONTENTS;
for(unsigned i = 0; i < RAM_SIZE; i++)
mRamData[i] = DEFAULT_RAM_CONTENTS;
if(mFileSize)
if(mRamXORData)
{
for(unsigned i = 0; i < RAM_SIZE; i++)
mRamData[i] ^= mRamXORData[i];

View File

@ -57,7 +57,7 @@ class CRam : public CLynxBase
public:
enum { HEADER_RAW_SIZE = 10 };
CRam(const uint8 *filememory,uint32 filesize) MDFN_COLD;
CRam(MDFNFILE *fp) MDFN_COLD;
~CRam() MDFN_COLD;
static bool TestMagic(const uint8* data, uint64 test_size) MDFN_COLD;
@ -65,8 +65,8 @@ class CRam : public CLynxBase
void Reset(void) MDFN_COLD;
void Poke(uint32 addr, uint8 data){ mRamData[addr]=data;};
uint8 Peek(uint32 addr){ return(mRamData[addr]);};
void Poke(uint32 addr, uint8 data){ mRamData[(uint16)addr]=data;};
uint8 Peek(uint32 addr){ return(mRamData[(uint16)addr]);};
uint32 ReadCycle(void) {return 5;};
uint32 WriteCycle(void) {return 5;};
uint32 ObjectSize(void) {return RAM_SIZE;};
@ -81,7 +81,6 @@ class CRam : public CLynxBase
uint8 mRamData[RAM_SIZE];
uint8 *mRamXORData;
uint16 boot_addr;
uint32 mFileSize;
uint32 mCRC32;
};

View File

@ -54,33 +54,34 @@
CRom::CRom(const char *romfile)
{
unsigned loop;
mWriteEnable=false;
Reset();
mWriteEnable=false;
Reset();
// Initialise ROM
for(loop = 0;loop < ROM_SIZE;loop++)
mRomData[loop]=DEFAULT_ROM_CONTENTS;
// Initialise ROM
for(int loop=0;loop<ROM_SIZE;loop++) mRomData[loop]=DEFAULT_ROM_CONTENTS;
// Load up the file
MDFNFILE *BIOSFile = file_open(romfile);
if(!BIOSFile)
{
MDFNFILE *BIOSFile = file_open(romfile);
if(!BIOSFile)
{
/* Could not open Lynx boot ROM. */
return;
}
return;
}
if(BIOSFile->size < 512)
{
if(BIOSFile->size < 512)
{
/* Lynx Boot ROM image is of an incorrect size. */
return;
return;
}
// memcpy(mRomData, BIOSFile->data, 512);
file_read(BIOSFile, mRomData, 512, 1);
file_close(BIOSFile);
}
memcpy(mRomData, BIOSFile->data, 512);
file_close(BIOSFile);
}
void CRom::Reset(void)

View File

@ -98,26 +98,6 @@ enum {sprite_background_shadow=0,
sprite_xor_shadow,
sprite_shadow};
/* Define register typedefs */
typedef struct
{
union
{
struct
{
#ifdef MSB_FIRST
uint8 High;
uint8 Low;
#else
uint8 Low;
uint8 High;
#endif
} Union8;
uint16 Val16;
};
} Uuint16;
typedef struct
{
union

478
mednafen/lynx/system.cpp Normal file
View File

@ -0,0 +1,478 @@
//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//////////////////////////////////////////////////////////////////////////////
// Handy - An Atari Lynx Emulator //
// Copyright (c) 1996,1997 //
// K. Wilkins //
//////////////////////////////////////////////////////////////////////////////
// System object class //
//////////////////////////////////////////////////////////////////////////////
// //
// This class provides the glue to bind of of the emulation objects //
// together via peek/poke handlers and pass thru interfaces to lower //
// objects, all control of the emulator is done via this class. Update() //
// does most of the work and each call emulates one CPU instruction and //
// updates all of the relevant hardware if required. It must be remembered //
// that if that instruction involves setting SPRGO then, it will cause a //
// sprite painting operation and then a corresponding update of all of the //
// hardware which will usually involve recursive calls to Update, see //
// Mikey SPRGO code for more details. //
// //
// K. Wilkins //
// August 1997 //
// //
//////////////////////////////////////////////////////////////////////////////
// Revision History: //
// ----------------- //
// //
// 01Aug1997 KW Document header added & class documented. //
// //
//////////////////////////////////////////////////////////////////////////////
#define SYSTEM_CPP
//#include <crtdbg.h>
//#define TRACE_SYSTEM
#include "mednafen/lynx/system.h"
#include "mednafen/mednafen-endian.h"
#include "mednafen/general.h"
#include "mednafen/mempatcher.h"
#include "mednafen/md5.h"
CSystem::CSystem(MDFNFILE *fp)
:mCart(NULL),
mRom(NULL),
mMemMap(NULL),
mRam(NULL),
mCpu(NULL),
mMikie(NULL),
mSusie(NULL)
{
mFileType=HANDY_FILETYPE_ILLEGAL;
char clip[11];
file_read(fp, clip, 11, 1);
file_seek(fp, 0, SEEK_SET);
clip[4]=0;
clip[10]=0;
if(!strcmp(&clip[6],"BS93"))
mFileType=HANDY_FILETYPE_HOMEBREW;
else if(!strcmp(&clip[0],"LYNX"))
mFileType=HANDY_FILETYPE_LNX;
else if(GET_FSIZE_PTR(fp)==128*1024 || GET_FSIZE_PTR(fp)==256*1024 || GET_FSIZE_PTR(fp)==512*1024)
/* Invalid Cart (type). but 128/256/512k size -> set to RAW and try to load raw rom image */
mFileType=HANDY_FILETYPE_RAW;
else
{
/* File format is unknown to module. This will then
* just load the core into an "Insert Game" screen */
}
MDFNMP_Init(65536, 1);
// Create the system objects that we'll use
// Attempt to load the cartridge errors caught above here...
mRom = new CRom(MDFN_MakeFName(MDFNMKF_FIRMWARE, 0, "lynxboot.img").c_str());
// An exception from this will be caught by the level above
switch(mFileType)
{
case HANDY_FILETYPE_RAW:
case HANDY_FILETYPE_LNX:
mCart = new CCart(fp);
mRam = new CRam(NULL);
break;
case HANDY_FILETYPE_HOMEBREW:
mCart = new CCart(NULL);
mRam = new CRam(fp);
break;
case HANDY_FILETYPE_SNAPSHOT:
case HANDY_FILETYPE_ILLEGAL:
default:
mCart = new CCart(NULL);
mRam = new CRam(NULL);
break;
}
// These can generate exceptions
mMikie = new CMikie(*this);
mSusie = new CSusie(*this);
// Instantiate the memory map handler
mMemMap = new CMemMap(*this);
// Now the handlers are set we can instantiate the CPU as is will use handlers on reset
mCpu = new C65C02(*this);
// Now init is complete do a reset, this will cause many things to be reset twice
// but what the hell, who cares, I don't.....
Reset();
}
CSystem::~CSystem()
{
// Cleanup all our objects
if(mCart!=NULL) delete mCart;
if(mRom!=NULL) delete mRom;
if(mRam!=NULL) delete mRam;
if(mCpu!=NULL) delete mCpu;
if(mMikie!=NULL) delete mMikie;
if(mSusie!=NULL) delete mSusie;
if(mMemMap!=NULL) delete mMemMap;
}
void CSystem::Reset(void)
{
mMikie->startTS -= gSystemCycleCount;
gSystemCycleCount=0;
gNextTimerEvent=0;
gCPUBootAddress=0;
gSystemIRQ=false;
gSystemNMI=false;
gSystemCPUSleep=false;
gSystemHalt=false;
gSuzieDoneTime = 0;
mMemMap->Reset();
mCart->Reset();
mRom->Reset();
mRam->Reset();
mMikie->Reset();
mSusie->Reset();
mCpu->Reset();
// Homebrew hashup
if(mFileType==HANDY_FILETYPE_HOMEBREW)
{
mMikie->PresetForHomebrew();
C6502_REGS regs;
mCpu->GetRegs(regs);
regs.PC=(uint16)gCPUBootAddress;
mCpu->SetRegs(regs);
}
}
// Somewhat of a hack to make sure undrawn lines are black.
bool LynxLineDrawn[256];
CSystem *lynxie = NULL;
static bool TestMagic(const char *name, MDFNFILE *fp)
{
uint8 data[64];
uint64 rc;
rc = fp->size;
if(rc >= CCart::HEADER_RAW_SIZE && CCart::TestMagic(data, sizeof(data)))
return true;
if(rc >= CRam::HEADER_RAW_SIZE && CRam::TestMagic(data, sizeof(data)))
return true;
return false;
}
static void Cleanup(void)
{
if(lynxie)
{
delete lynxie;
lynxie = NULL;
}
}
void Load(MDFNFILE *fp)
{
lynxie = new CSystem(fp);
switch(lynxie->CartGetRotate())
{
case CART_ROTATE_LEFT:
MDFNGameInfo->rotated = MDFN_ROTATE270;
break;
case CART_ROTATE_RIGHT:
MDFNGameInfo->rotated = MDFN_ROTATE90;
break;
}
if(lynxie->mRam->InfoRAMSize)
{
memcpy(MDFNGameInfo->MD5, lynxie->mRam->MD5, 16);
MDFN_printf(_("RAM: %u bytes\n"), lynxie->mRam->InfoRAMSize);
MDFN_printf(_("CRC32: 0x%08x\n"), lynxie->mRam->CRC32());
MDFN_printf(_("RAM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());
}
else
{
memcpy(MDFNGameInfo->MD5, lynxie->mCart->MD5, 16);
MDFN_printf(_("ROM: %dKiB\n"), (lynxie->mCart->InfoROMSize + 1023) / 1024);
MDFN_printf(_("CRC32: 0x%08x\n"), lynxie->mCart->CRC32());
MDFN_printf(_("ROM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());
}
MDFNGameInfo->fps = (uint32)(59.8 * 65536 * 256);
if(MDFN_GetSettingB("lynx.lowpass"))
{
lynxie->mMikie->miksynth.treble_eq(-35);
}
else
{
lynxie->mMikie->miksynth.treble_eq(0);
}
}
void CloseGame(void)
{
Cleanup();
}
static uint8 *chee;
void Emulate(EmulateSpecStruct *espec)
{
espec->DisplayRect.x = 0;
espec->DisplayRect.y = 0;
espec->DisplayRect.w = 160;
espec->DisplayRect.h = 102;
if(espec->VideoFormatChanged)
lynxie->DisplaySetAttributes(espec->surface->format); // FIXME, pitch
if(espec->SoundFormatChanged)
{
lynxie->mMikie->mikbuf.set_sample_rate(espec->SoundRate ? espec->SoundRate : 44100, 60);
lynxie->mMikie->mikbuf.clock_rate((long int)(16000000 / 4));
lynxie->mMikie->mikbuf.bass_freq(60);
lynxie->mMikie->miksynth.volume(0.50);
}
uint16 butt_data = chee[0] | (chee[1] << 8);
lynxie->SetButtonData(butt_data);
MDFNMP_ApplyPeriodicCheats();
memset(LynxLineDrawn, 0, sizeof(LynxLineDrawn[0]) * 102);
lynxie->mMikie->mpSkipFrame = espec->skip;
lynxie->mMikie->mpDisplayCurrent = espec->surface;
lynxie->mMikie->mpDisplayCurrentLine = 0;
lynxie->mMikie->startTS = gSystemCycleCount;
while(lynxie->mMikie->mpDisplayCurrent && (gSystemCycleCount - lynxie->mMikie->startTS) < 700000)
{
lynxie->Update();
// printf("%d ", gSystemCycleCount - lynxie->mMikie->startTS);
}
{
// FIXME, we should integrate this into mikie.*
uint32 color_black = espec->surface->MakeColor(30, 30, 30);
for(int y = 0; y < 102; y++)
{
if(espec->surface->format.bpp == 16)
{
uint16 *row = espec->surface->pixels16 + y * espec->surface->pitchinpix;
if(!LynxLineDrawn[y])
{
for(int x = 0; x < 160; x++)
row[x] = color_black;
}
}
else
{
uint32 *row = espec->surface->pixels + y * espec->surface->pitchinpix;
if(!LynxLineDrawn[y])
{
for(int x = 0; x < 160; x++)
row[x] = color_black;
}
}
}
}
espec->MasterCycles = gSystemCycleCount - lynxie->mMikie->startTS;
if(espec->SoundBuf)
{
lynxie->mMikie->mikbuf.end_frame((gSystemCycleCount - lynxie->mMikie->startTS) >> 2);
espec->SoundBufSize = lynxie->mMikie->mikbuf.read_samples(espec->SoundBuf, espec->SoundBufMaxSize) / 2; // divide by nr audio chn
}
else
espec->SoundBufSize = 0;
}
void SetInput(unsigned port, const char *type, uint8 *ptr)
{
chee = (uint8 *)ptr;
}
static void TransformInput(void)
{
if(MDFN_GetSettingB("lynx.rotateinput"))
{
static const unsigned bp[4] = { 4, 6, 5, 7 };
const unsigned offs = MDFNGameInfo->rotated;
uint16 butt_data = MDFN_de16lsb(chee);
butt_data = (butt_data & 0xFF0F) |
(((butt_data >> bp[0]) & 1) << bp[(0 + offs) & 3]) |
(((butt_data >> bp[1]) & 1) << bp[(1 + offs) & 3]) |
(((butt_data >> bp[2]) & 1) << bp[(2 + offs) & 3]) |
(((butt_data >> bp[3]) & 1) << bp[(3 + offs) & 3]);
//printf("%d, %04x\n", MDFNGameInfo->rotated, butt_data);
MDFN_en16lsb(chee, butt_data);
}
}
int StateAction(StateMem *sm, int load, int data_only)
{
SFORMAT SystemRegs[] =
{
SFVAR(gSuzieDoneTime),
SFVAR(gSystemCycleCount),
SFVAR(gNextTimerEvent),
SFVAR(gCPUBootAddress),
SFVAR(gSystemIRQ),
SFVAR(gSystemNMI),
SFVAR(gSystemCPUSleep),
SFVAR(gSystemHalt),
SFARRAYN(lynxie->GetRamPointer(), RAM_SIZE, "RAM"),
SFEND
};
int ret = MDFNSS_StateAction(sm, load, data_only, SystemRegs, "SYST", false);
ret &= lynxie->mSusie->StateAction(sm, load, data_only);
ret &= lynxie->mMemMap->StateAction(sm, load, data_only);
ret &= lynxie->mCart->StateAction(sm, load, data_only);
ret &= lynxie->mMikie->StateAction(sm, load, data_only);
ret &= lynxie->mCpu->StateAction(sm, load, data_only);
return ret;
}
static void SetLayerEnableMask(uint64 mask)
{
}
void DoSimpleCommand(int cmd)
{
switch(cmd)
{
case MDFN_MSC_POWER:
case MDFN_MSC_RESET: lynxie->Reset(); break;
}
}
static MDFNSetting LynxSettings[] =
{
{ "lynx.rotateinput", MDFNSF_NOFLAGS, "Virtually rotate D-pad along with screen.", NULL, MDFNST_BOOL, "1" },
{ "lynx.lowpass", MDFNSF_CAT_SOUND, "Enable sound output lowpass filter.", NULL, MDFNST_BOOL, "1" },
{ NULL }
};
static const InputDeviceInputInfoStruct IDII[] =
{
{ "a", "A (outer)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
{ "b", "B (inner)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
{ "option_2", "Option 2 (lower)", 5, IDIT_BUTTON_CAN_RAPID, NULL },
{ "option_1", "Option 1 (upper)", 4, IDIT_BUTTON_CAN_RAPID, NULL },
{ "left", "LEFT ←", /*VIRTB_DPAD0_L,*/ 2, IDIT_BUTTON, "right", { "up", "right", "down" } },
{ "right", "RIGHT →", /*VIRTB_DPAD0_R,*/ 3, IDIT_BUTTON, "left", { "down", "left", "up" } },
{ "up", "UP ↑", /*VIRTB_DPAD0_U,*/ 0, IDIT_BUTTON, "down", { "right", "down", "left" } },
{ "down", "DOWN ↓", /*VIRTB_DPAD0_D,*/ 1, IDIT_BUTTON, "up", { "left", "up", "right" } },
{ "pause", "PAUSE", 6, IDIT_BUTTON, NULL },
};
static InputDeviceInfoStruct InputDeviceInfo[] =
{
{
"gamepad",
"Gamepad",
NULL,
NULL,
sizeof(IDII) / sizeof(InputDeviceInputInfoStruct),
IDII,
}
};
static const InputPortInfoStruct PortInfo[] =
{
{ "builtin", "Built-In", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo, 0 }
};
static InputInfoStruct InputInfo =
{
sizeof(PortInfo) / sizeof(InputPortInfoStruct),
PortInfo
};
static const FileExtensionSpecStruct KnownExtensions[] =
{
{ ".lnx", "Atari Lynx ROM Image" },
{ NULL, NULL }
};
MDFNGI EmulatedLynx =
{
LynxSettings,
MDFN_MASTERCLOCK_FIXED(16000000),
0,
false, // Multires possible?
160, // lcm_width
102, // lcm_height
NULL, // Dummy
160, // Nominal width
102, // Nominal height
160, // Framebuffer width
102, // Framebuffer height
2, // Number of output sound channels
};

View File

@ -114,7 +114,7 @@ class CSystem;
class CSystem : public CSystemBase
{
public:
CSystem(const uint8 *, int32) MDFN_COLD;
CSystem(MDFNFILE *fp) MDFN_COLD;
~CSystem() MDFN_COLD;
public:
@ -220,4 +220,13 @@ class CSystem : public CSystemBase
extern bool LynxLineDrawn[256];
void Load(MDFNFILE *fp);
void CloseGame(void);
void Emulate(EmulateSpecStruct *espec);
void SetInput(unsigned port, const char *type, uint8 *ptr);
int StateAction(StateMem *sm, const unsigned load, const bool data_only);
void DoSimpleCommand(int cmd);
extern CSystem *lynxie;
#endif

View File

@ -121,6 +121,27 @@ class MDFN_Surface //typedef struct
uint16 *pixels16;
uint32 *pixels;
private:
INLINE void pix_(uint16*& z) const { z = pixels16; }
INLINE void pix_(uint32*& z) const { z = pixels; }
public:
template<typename T>
INLINE const T* pix(void) const
{
T* ret;
pix_(ret);
return (const T*)ret;
}
template<typename T>
INLINE T* pix(void)
{
T* ret;
pix_(ret);
return ret;
}
// w, h, and pitch32 should always be > 0
int32 w;
int32 h;