mirror of
https://github.com/libretro/beetle-lynx-libretro.git
synced 2024-11-27 02:12:46 +00:00
Merge pull request #35 from negativeExponent/update
Sync to upstream 1.24.0 and add misc features
This commit is contained in:
commit
46ac62b4c3
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@
|
||||
*.dll
|
||||
*.dylib
|
||||
/old
|
||||
|
||||
.vscode
|
||||
.clang-format
|
||||
|
3
Makefile
3
Makefile
@ -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)
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -34,4 +34,3 @@ in a public API, you may need this.
|
||||
#include <compat/msvc.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -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
|
||||
|
114
libretro-common/include/retro_environment.h
Normal file
114
libretro-common/include/retro_environment.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
111
libretro-common/include/vfs/vfs.h
Normal file
111
libretro-common/include/vfs/vfs.h
Normal 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
|
@ -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
|
||||
|
@ -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
599
libretro.cpp
599
libretro.cpp
@ -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
264
libretro_core_options.h
Normal 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
|
80
libretro_core_options_intl.h
Normal file
80
libretro_core_options_intl.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -395,8 +395,7 @@ class CMikie : public CLynxBase
|
||||
uint32 mLynxLineDMACounter;
|
||||
uint32 mLynxAddr;
|
||||
|
||||
void CopyLineSurface16(void);
|
||||
void CopyLineSurface32(void);
|
||||
template<typename T> void CopyLineSurface(void);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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
478
mednafen/lynx/system.cpp
Normal 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
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user