diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..512e2df Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 85a74d7..9b33691 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ snap/ *.iso *.mcd *.rom -*.o \ No newline at end of file +*.o +psxe.app \ No newline at end of file diff --git a/Info.plist b/Info.plist index 5cfc5cf..a284091 100644 --- a/Info.plist +++ b/Info.plist @@ -1,32 +1,26 @@ - - CFBundleDevelopmentRegion - en - CFBundleExecutable - psxe - CFBundleIdentifier - psxe.app.0 - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - psxe - CFBundlePackageType - APPL - CFBundleShortVersionString - 0.1-alpha - CFBundleSignature - psxe - CFBundleVersion - 0.1-alpha - LSApplicationCategoryType - public.app-category.games - LSMinimumSystemVersion - 10.6 - NSHighResolutionCapable - - NSPrincipalClass - NSApplication - + + CFBundleDevelopmentRegion + en + CFBundleExecutable + psxe + CFBundleIdentifier + psxe.app.0 + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + psxe + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.10.4-alpha + CFBundleVersion + 0.10.4-alpha + LSMinimumSystemVersion + 10.9 + NSHighResolutionCapable + + diff --git a/Makefile b/Makefile index 81289c2..4a0e3db 100644 --- a/Makefile +++ b/Makefile @@ -1,35 +1,65 @@ .ONESHELL: -CFLAGS := -g -DLOG_USE_COLOR `sdl2-config --cflags --libs` -CFLAGS += -Ofast -Wno-overflow -Wall -pedantic -Wno-address-of-packed-member -flto - -PLATFORM := $(shell uname -s) - -ifeq ($(PLATFORM),Darwin) - CFLAGS += -mmacosx-version-min=10.9 -Wno-newline-eof -endif - -VERSION_TAG := $(shell git describe --always --tags --abbrev=0) -COMMIT_HASH := $(shell git rev-parse --short HEAD) -OS_INFO := $(shell uname -rmo) - -SOURCES := $(wildcard psx/*.c) +CFLAGS := -g -DLOG_USE_COLOR `sdl2-config --cflags --libs` +CFLAGS += -Ofast -Wno-overflow -Wall -pedantic -Wno-address-of-packed-member -flto + +PLATFORM := $(shell uname -s) + +ifeq ($(PLATFORM),Darwin) + CFLAGS += -mmacosx-version-min=10.9 -Wno-newline-eof +endif + +SHARED_EXT := .so +SHARED_LDFLAGS := -shared +SHARED_CFLAGS := $(CFLAGS) -D__DLL_BUILD -fPIC + +ifeq ($(PLATFORM),Darwin) + SHARED_EXT := .dylib + SHARED_LDFLAGS := -dynamiclib +endif + +VERSION_TAG := $(shell git describe --always --tags --abbrev=0) +COMMIT_HASH := $(shell git rev-parse --short HEAD) +OS_INFO := $(shell uname -rmo) + +SOURCES := $(wildcard psx/*.c) SOURCES += $(wildcard psx/dev/*.c) -SOURCES += $(wildcard psx/dev/cdrom/*.c) -SOURCES += $(wildcard psx/input/*.c) -SOURCES += $(wildcard psx/disc/*.c) -SOURCES += $(wildcard frontend/*.c) - -bin/psxe frontend/main.c: - mkdir -p bin - - gcc $(SOURCES) -o bin/psxe \ - -I"." \ - -I"psx" \ - -DOS_INFO="$(OS_INFO)" \ - -DREP_VERSION="$(VERSION_TAG)" \ - -DREP_COMMIT_HASH="$(COMMIT_HASH)" \ - $(CFLAGS) - -clean: - rm -rf "bin" +SOURCES += $(wildcard psx/dev/cdrom/*.c) +SOURCES += $(wildcard psx/input/*.c) +SOURCES += $(wildcard psx/disc/*.c) +SOURCES += $(wildcard frontend/*.c) +SHARED_SOURCES := $(filter-out frontend/main.c,$(SOURCES)) + +BIN_DIR := bin +BIN := $(BIN_DIR)/psxe +SHARED_BIN := $(BIN_DIR)/libpsxe$(SHARED_EXT) + +.PHONY: all clean shared + +all: $(BIN) + +$(BIN_DIR): + mkdir -p $(BIN_DIR) + +$(BIN): $(SOURCES) | $(BIN_DIR) + gcc $(SOURCES) -o $(BIN) \ + -I"." \ + -I"psx" \ + -DOS_INFO="$(OS_INFO)" \ + -DREP_VERSION="$(VERSION_TAG)" \ + -DREP_COMMIT_HASH="$(COMMIT_HASH)" \ + $(CFLAGS) + +shared: $(SHARED_BIN) + +$(SHARED_BIN): $(SHARED_SOURCES) | $(BIN_DIR) + gcc $(SHARED_LDFLAGS) $(SHARED_SOURCES) -o $(SHARED_BIN) \ + -I"." \ + -I"psx" \ + -DOS_INFO="$(OS_INFO)" \ + -DREP_VERSION="$(VERSION_TAG)" \ + -DREP_COMMIT_HASH="$(COMMIT_HASH)" \ + $(SHARED_CFLAGS) + +clean: + rm -rf "$(BIN_DIR)" diff --git a/README.md b/README.md index 96ba4c1..a44e617 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ A simple and portable Sony PlayStation emulator and emulation library written in ![macOS](https://github.com/allkern/psx/actions/workflows/macos.yml/badge.svg) ![Ubuntu](https://github.com/allkern/psx/actions/workflows/ubuntu.yml/badge.svg) -## Running -You can download the latest automated build for your platform on Releases. If your system isn't supported, you can easily build the emulator from source, instructions on "Building" below. - -In order to run the emulator, you will need a BIOS file. You can either get one from the internet or [dump it from your own console](https://www.youtube.com/watch?v=u8eHp0COcBo). - +## Running +You can download the latest automated build for your platform on Releases. If your system isn't supported, you can easily build the emulator from source, instructions on "Building" below. + +In order to run the emulator, you will need a BIOS file. You can either get one from the internet or [dump it from your own console](https://www.youtube.com/watch?v=u8eHp0COcBo). + Most BIOS versions are confirmed to work. Use the `-b` or `--bios` setting to configure the BIOS file. @@ -72,12 +72,47 @@ cd psxe make clean && make ``` -### macOS -``` -git clone https://github.com/allkern/psxe -cd psxe -./build.sh -``` +### macOS +``` +git clone https://github.com/allkern/psxe +cd psxe +./build.sh +``` + +## Configuration + +A `settings.toml` is generated on first run next to the executable. CLI flags always override file settings for a session. + +### Settings file keys +- `psxe_version` (string, managed by PSXE) + +`[bios]` +- `search_path` (string): directory to search for BIOS files +- `preferred_model` (string): default model (e.g. `SCPH-1001`) +- `override_file` (string): explicit BIOS path; leave empty to use search/model + +`[console]` +- `region` (string): `ntsc`, `pal`, or `auto` + +`[video]` +- `texture_scale_mode` (bool): enable SDL texture scale mode; toggle bilinear via F4 when supported + +### CLI flags (override settings) +`psxe [options] path-to-cdrom` +- `-a, --use-args` : ignore settings file +- `-b, --bios=` : BIOS file +- `-B, --bios-folder` : BIOS search folder +- `-c, --console-source=` : auto | null | kernel | atcons +- `-e, --exp-rom=` : expansion ROM file +- `-L, --log-level=` : 0=trace .. 5=fatal +- `-M, --model=` : console model (SCPH-XXXX) +- `-r, --region=` : console region +- `-s, --scale=` : display scaling factor +- `-S, --settings-file=` : settings file path +- `-q, --quiet` : silence logs +- `-x, --exe=` : PS-X EXE to boot +- `--cdrom=` : CDROM image +- `-h/--help` | `--help-model` | `--help-region` | `-v/--version` : info helpers ## Acknowledgements This project uses external open source code that can be found on the following GitHub repos: diff --git a/build.sh b/build.sh index f1838e5..e99e2cc 100755 --- a/build.sh +++ b/build.sh @@ -1,19 +1,36 @@ #!/bin/sh -# Build emulator -make clean && make +# Build options: +# BUILD_DLL=1 -> build shared library (libpsxe.* with __DLL_BUILD) +# BUILD_DLL_ONLY=1 -> build only the shared library +# BUNDLE_APP=1 -> bundle macOS .app (uses executable build) -# Create bundle filesystem -mkdir -p psxe.app/Contents/MacOS/Libraries +make clean -# Move executable to folder -mv bin/psxe psxe.app/Contents/MacOS +if [ "${BUILD_DLL_ONLY:-0}" != "0" ]; then + make shared +else + make + if [ "${BUILD_DLL:-0}" != "0" ]; then + make shared + fi -# Make executable -chmod 777 psxe.app/Contents/MacOS/psxe + if [ "${BUNDLE_APP:-0}" != "0" ]; then + # Create bundle filesystem + mkdir -p psxe.app/Contents/MacOS/Libraries -# Bundle required dylibs -dylibbundler -b -x ./psxe.app/Contents/MacOS/psxe -d ./psxe.app/Contents/Libraries/ -p @executable_path/../Libraries/ -cd + # Copy executable to bundle folder (keep bin/psxe intact) + cp bin/psxe psxe.app/Contents/MacOS -# Move plist to Contents folder -mv Info.plist psxe.app/Contents/Info.plist \ No newline at end of file + # Make executable + chmod 777 psxe.app/Contents/MacOS/psxe + + # Bundle required dylibs + dylibbundler -b -x ./psxe.app/Contents/MacOS/psxe -d ./psxe.app/Contents/Libraries/ -p @executable_path/../Libraries/ -cd + + # Copy plist to Contents folder (keep a copy in repo for subsequent builds) + cp Info.plist psxe.app/Contents/Info.plist + else + echo "Skipping macOS .app bundle. Set BUNDLE_APP=1 to build the application bundle." + fi +fi diff --git a/frontend/config.c b/frontend/config.c index fbdb49c..b0ea055 100644 --- a/frontend/config.c +++ b/frontend/config.c @@ -19,11 +19,11 @@ static const char* g_version_text = "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"; -static const char g_default_settings[] = - "# Settings file generated by PSXE\n" - "\n" - "# Don't change please, reserved for future use\n" - "psxe_version = \"" STR(REP_VERSION) "\"\n" +static const char g_default_settings[] = + "# Settings file generated by PSXE\n" + "\n" + "# Don't change please, reserved for future use\n" + "psxe_version = \"" STR(REP_VERSION) "\"\n" "\n" "# BIOS-related settings:\n" "[bios]\n" @@ -31,9 +31,13 @@ static const char g_default_settings[] = " preferred_model = \"SCPH-1001\"\n" " override_file = \"\"\n" "\n" - "# Console settings\n" - "[console]\n" - " region = \"auto\"\n"; + "# Console settings\n" + "[console]\n" + " region = \"auto\"\n" + "\n" + "# Video settings\n" + "[video]\n" + " texture_scale_mode = false\n"; static const char* g_models_text = "Available console models:\n" @@ -78,8 +82,8 @@ void psxe_cfg_destroy(psxe_config_t* cfg) { free(cfg); } -void psxe_cfg_load_defaults(psxe_config_t* cfg) { - cfg->bios = "bios.bin"; +void psxe_cfg_load_defaults(psxe_config_t* cfg) { + cfg->bios = "bios.bin"; cfg->bios_search = "bios"; cfg->exe = NULL; cfg->help_model = 0; @@ -91,25 +95,39 @@ void psxe_cfg_load_defaults(psxe_config_t* cfg) { cfg->settings_path = NULL; cfg->use_args = 0; cfg->version = 0; - cfg->log_level = LOG_FATAL; - cfg->quiet = 0; - cfg->cd_path = NULL; - cfg->exp_path = NULL; -} + cfg->log_level = LOG_FATAL; + cfg->quiet = 0; + cfg->cd_path = NULL; + cfg->exp_path = NULL; + cfg->texture_scale_mode = 0; +} -void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) { - log_set_level(LOG_INFO); +void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) { + log_set_level(LOG_INFO); + + // macOS .app bundles can inject a "-psn_*" argument; drop it before parsing + const char** argv_filtered = (const char**)malloc(sizeof(char*) * argc); + int argc_filtered = 0; + + for (int i = 0; i < argc; i++) { + if (strncmp(argv[i], "-psn_", 5) == 0) + continue; + + argv_filtered[argc_filtered++] = argv[i]; + } - int use_args = 0; - int version = 0; + int use_args = 0; + int version = 0; int help_model = 0; int help_region = 0; int log_level = 0; - int quiet = 0; - int console_source = 0; - int scale = 0; - const char* settings_path = NULL; - const char* bios = NULL; + int quiet = 0; + int console_source = 0; + int scale = 0; + int texture_scale_mode = 0; + const char* settings_path = NULL; + const char* bios = NULL; + int bios_from_cli = 0; const char* bios_search = NULL; const char* model = NULL; const char* exe = NULL; @@ -130,7 +148,7 @@ void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) { OPT_BOOLEAN ('v', "version" , &version , "Display version and build information", NULL, 0, 0), OPT_GROUP("Basic options"), OPT_BOOLEAN ('a', "use-args" , &use_args , "Ignore settings file, use CLI args instead", NULL, 0, 0), - OPT_STRING ('b', "bios" , &bios , "Specify a BIOS file (ignores -B, -M)", NULL, 0, 0), + OPT_STRING ('b', "bios" , &bios , "Specify a BIOS file (ignores -B, -M)", NULL, 0, 0), OPT_BOOLEAN ('B', "bios-folder" , &bios_search , "Specify a BIOS search folder", NULL, 0, 0), OPT_STRING ('c', "console-source", &console_source, "Select console source (auto, null, kernel, atcons)"), OPT_STRING ('e', "exp-rom" , &exp_path , "Specify an expansion ROM file"), @@ -147,10 +165,13 @@ void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) { struct argparse argparse; - argparse_init(&argparse, options, usages, 0); - argparse_describe(&argparse, NULL, g_desc_text); - - argc = argparse_parse(&argparse, argc, argv); + argparse_init(&argparse, options, usages, 0); + argparse_describe(&argparse, NULL, g_desc_text); + + argc = argparse_parse(&argparse, argc_filtered, argv_filtered); + + if (bios) + bios_from_cli = 1; if (help_model) { printf("%s\n", g_models_text); @@ -201,10 +222,10 @@ void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) { toml_table_t* conf = toml_parse_file(settings, error, sizeof(error)); if (!conf) { - log_error("Couldn't parse settings file"); - - exit(1); - } + log_error("Couldn't parse settings file"); + + exit(1); + } toml_datum_t s_version = toml_string_in(conf, "psxe_version"); @@ -227,27 +248,36 @@ void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) { if (s_bios_preferred_model.ok) model = s_bios_preferred_model.u.s; - toml_datum_t s_bios_override_file = toml_string_in(s_bios_table, "override_file"); - - if (s_bios_override_file.ok) - bios = s_bios_override_file.u.s; + toml_datum_t s_bios_override_file = toml_string_in(s_bios_table, "override_file"); + + if (!bios_from_cli && s_bios_override_file.ok && s_bios_override_file.u.s[0]) + bios = s_bios_override_file.u.s; } toml_table_t* s_console_table = toml_table_in(conf, "console"); - if (s_console_table) { - toml_datum_t s_console_region = toml_string_in(s_console_table, "region"); - - if (s_console_region.ok) - region = s_console_region.u.s; - } - - psxe_version = s_version.u.s; + if (s_console_table) { + toml_datum_t s_console_region = toml_string_in(s_console_table, "region"); + + if (s_console_region.ok) + region = s_console_region.u.s; + } + + toml_table_t* s_video_table = toml_table_in(conf, "video"); + + if (s_video_table) { + toml_datum_t s_texture_scale_mode = toml_bool_in(s_video_table, "texture_scale_mode"); + + if (s_texture_scale_mode.ok) + texture_scale_mode = s_texture_scale_mode.u.b; + } + + psxe_version = s_version.u.s; log_info("Settings file parsed. PSXE version: %s", psxe_version); - fclose(settings); - } + fclose(settings); + } if (argc) { if (argc > 1) { @@ -283,12 +313,18 @@ void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) { if (psxe_version) cfg->psxe_version = psxe_version; - if (exp_path) - cfg->exp_path = exp_path; - - if (scale) - cfg->scale = scale; -} + if (exp_path) + cfg->exp_path = exp_path; + + if (scale) + cfg->scale = scale; + + cfg->texture_scale_mode = texture_scale_mode; + + log_info("Using BIOS path: %s", cfg->bios ? cfg->bios : "(none)"); + + free(argv_filtered); +} // To-do: Implement BIOS searching char* psxe_cfg_get_bios_path(psxe_config_t* cfg) { @@ -296,4 +332,4 @@ char* psxe_cfg_get_bios_path(psxe_config_t* cfg) { } #undef STR1 -#undef STR \ No newline at end of file +#undef STR diff --git a/frontend/config.h b/frontend/config.h index 81f565d..70fef76 100644 --- a/frontend/config.h +++ b/frontend/config.h @@ -10,16 +10,17 @@ typedef struct { int use_args; int version; int help_model; - int help_region; - int log_level; - int quiet; - int console_source; - int scale; - const char* snap_path; - const char* settings_path; - const char* bios; - const char* bios_search; - const char* model; + int help_region; + int log_level; + int quiet; + int console_source; + int scale; + int texture_scale_mode; + const char* snap_path; + const char* settings_path; + const char* bios; + const char* bios_search; + const char* model; const char* exe; const char* region; const char* psxe_version; @@ -34,4 +35,4 @@ void psxe_cfg_load(psxe_config_t*, int, const char**); char* psxe_cfg_get_bios_path(psxe_config_t*); void psxe_cfg_destroy(psxe_config_t*); -#endif \ No newline at end of file +#endif diff --git a/frontend/main.c b/frontend/main.c index 4c2de72..e13328b 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -37,21 +37,26 @@ int main(int argc, const char* argv[]) { log_set_level(cfg->log_level); - psx_t* psx = psx_create(); - psx_init(psx, cfg->bios, cfg->exp_path); + psx_t* psx = psx_create(); + int init_result = psx_init(psx, cfg->bios, cfg->exp_path); + + if (init_result) { + log_fatal("Failed to initialize PSX (code %d). Check BIOS/expansion paths and settings.", init_result); + return 1; + } psx_cdrom_t* cdrom = psx_get_cdrom(psx); // To-do: Set CDROM firmware version and region based // on CLI options - if (cfg->cd_path) - psx_cdrom_open(cdrom, cfg->cd_path); - - psxe_screen_t* screen = psxe_screen_create(); - psxe_screen_init(screen, psx); - psxe_screen_set_scale(screen, cfg->scale); - psxe_screen_reload(screen); + if (cfg->cd_path) + psx_cdrom_open(cdrom, cfg->cd_path); + + psxe_screen_t* screen = psxe_screen_create(); + psxe_screen_init(screen, psx, cfg); + psxe_screen_set_scale(screen, cfg->scale); + psxe_screen_reload(screen); SDL_Init(SDL_INIT_AUDIO); @@ -126,4 +131,4 @@ int main(int argc, const char* argv[]) { psxe_screen_destroy(screen); return 0; -} \ No newline at end of file +} diff --git a/frontend/screen.c b/frontend/screen.c index 6547c20..ff30692 100644 --- a/frontend/screen.c +++ b/frontend/screen.c @@ -1,7 +1,8 @@ -#include "screen.h" - -#include "input/sda.h" -#include "input/guncon.h" +#include "screen.h" + +#include "input/sda.h" +#include "input/guncon.h" +#include "../psx/log.h" uint32_t screen_get_button(SDL_Keycode k) { if (k == SDLK_x ) return PSXI_SW_SDA_CROSS; @@ -66,16 +67,30 @@ int screen_get_base_width(psxe_screen_t* screen) { return 320; } -psxe_screen_t* psxe_screen_create(void) { - return (psxe_screen_t*)malloc(sizeof(psxe_screen_t)); -} - -void psxe_screen_init(psxe_screen_t* screen, psx_t* psx) { - memset(screen, 0, sizeof(psxe_screen_t)); - - if (screen->debug_mode) { - screen->width = PSX_GPU_FB_WIDTH; - screen->height = PSX_GPU_FB_HEIGHT; +psxe_screen_t* psxe_screen_create(void) { + return (psxe_screen_t*)malloc(sizeof(psxe_screen_t)); +} + +static void psxe_screen_apply_texture_scale_mode(psxe_screen_t* screen) { +#if SDL_VERSION_ATLEAST(2, 0, 12) + if (!screen->texture_scale_mode) + return; + + SDL_ScaleMode scale_mode = screen->bilinear ? SDL_ScaleModeLinear : SDL_ScaleModeNearest; + + if (SDL_SetTextureScaleMode(screen->texture, scale_mode) < 0) { + log_warn("SDL_SetTextureScaleMode failed, disabling texture_scale_mode: %s", SDL_GetError()); + screen->texture_scale_mode = 0; + } +#endif +} + +void psxe_screen_init(psxe_screen_t* screen, psx_t* psx, const psxe_config_t* cfg) { + memset(screen, 0, sizeof(psxe_screen_t)); + + if (screen->debug_mode) { + screen->width = PSX_GPU_FB_WIDTH; + screen->height = PSX_GPU_FB_HEIGHT; } else { screen->width = 320; screen->height = 240; @@ -83,23 +98,29 @@ void psxe_screen_init(psxe_screen_t* screen, psx_t* psx) { screen->scale = 1; screen->open = 1; - screen->format = SDL_PIXELFORMAT_BGR555; - screen->psx = psx; - screen->pad = psx_get_pad(psx); - - screen->texture_width = PSX_GPU_FB_WIDTH; - screen->texture_height = PSX_GPU_FB_HEIGHT; + screen->format = SDL_PIXELFORMAT_BGR555; + screen->psx = psx; + screen->pad = psx_get_pad(psx); + screen->texture_scale_mode = cfg ? cfg->texture_scale_mode : 0; + screen->bilinear = screen->texture_scale_mode ? SDL_ScaleModeLinear : SDL_ScaleModeNearest; + + screen->texture_width = PSX_GPU_FB_WIDTH; + screen->texture_height = PSX_GPU_FB_HEIGHT; SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER); SDL_SetRenderDrawColor(screen->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); - screen->controller = screen_find_controller(); -} - -void psxe_screen_reload(psxe_screen_t* screen) { - if (screen->texture) SDL_DestroyTexture(screen->texture); - if (screen->renderer) SDL_DestroyRenderer(screen->renderer); - if (screen->window) SDL_DestroyWindow(screen->window); + screen->controller = screen_find_controller(); +} + +void psxe_screen_set_window_handle(psxe_screen_t* screen, void* native_handle) { + screen->external_window = native_handle; +} + +void psxe_screen_reload(psxe_screen_t* screen) { + if (screen->texture) SDL_DestroyTexture(screen->texture); + if (screen->renderer) SDL_DestroyRenderer(screen->renderer); + if (screen->window) SDL_DestroyWindow(screen->window); if (screen->debug_mode) { screen->width = PSX_GPU_FB_WIDTH; @@ -113,14 +134,30 @@ void psxe_screen_reload(psxe_screen_t* screen) { screen->height = 240; } } - - screen->window = SDL_CreateWindow( - "psxe " STR(REP_VERSION) "-" STR(REP_COMMIT_HASH), - SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - screen->width * screen->scale, - screen->height * screen->scale, - 0 - ); + +#ifndef __DLL_BUILD + screen->window = SDL_CreateWindow( + "psxe " STR(REP_VERSION) "-" STR(REP_COMMIT_HASH), + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + screen->width * screen->scale, + screen->height * screen->scale, + 0 + ); +#else + if (!screen->external_window) { + log_error("DLL build requires external window handle before reload"); + screen->open = 0; + return; + } + + screen->window = SDL_CreateWindowFrom(screen->external_window); + + if (!screen->window) { + log_error("Failed to create SDL window from external handle: %s", SDL_GetError()); + screen->open = 0; + return; + } +#endif screen->renderer = SDL_CreateRenderer( screen->window, @@ -128,16 +165,14 @@ void psxe_screen_reload(psxe_screen_t* screen) { SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC ); - screen->texture = SDL_CreateTexture( - screen->renderer, - screen->format, - SDL_TEXTUREACCESS_STREAMING, - screen->texture_width, screen->texture_height - ); - -#if SDL_VERSION_ATLEAST(2, 0, 12) - SDL_SetTextureScaleMode(screen->texture, screen->bilinear); -#endif + screen->texture = SDL_CreateTexture( + screen->renderer, + screen->format, + SDL_TEXTUREACCESS_STREAMING, + screen->texture_width, screen->texture_height + ); + + psxe_screen_apply_texture_scale_mode(screen); // Check for retina displays int width = 0, height = 0; @@ -511,19 +546,17 @@ void psxe_gpu_dmode_event_cb(psx_gpu_t* gpu) { SDL_DestroyTexture(screen->texture); - screen->texture = SDL_CreateTexture( - screen->renderer, - screen->format, - SDL_TEXTUREACCESS_STREAMING, - screen->texture_width, screen->texture_height - ); - -#if SDL_VERSION_ATLEAST(2, 0, 12) - SDL_SetTextureScaleMode(screen->texture, screen->bilinear); -#endif - - SDL_SetWindowSize(screen->window, screen->width, screen->height); -} + screen->texture = SDL_CreateTexture( + screen->renderer, + screen->format, + SDL_TEXTUREACCESS_STREAMING, + screen->texture_width, screen->texture_height + ); + + psxe_screen_apply_texture_scale_mode(screen); + + SDL_SetWindowSize(screen->window, screen->width, screen->height); +} void psxe_gpu_vblank_event_cb(psx_gpu_t* gpu) { psxe_screen_t* screen = gpu->udata[0]; @@ -531,4 +564,4 @@ void psxe_gpu_vblank_event_cb(psx_gpu_t* gpu) { psxe_screen_update(screen); psxe_gpu_vblank_timer_event_cb(gpu); -} \ No newline at end of file +} diff --git a/frontend/screen.h b/frontend/screen.h index e50fbc5..a7f87ab 100644 --- a/frontend/screen.h +++ b/frontend/screen.h @@ -1,8 +1,9 @@ #ifndef SCREEN_H #define SCREEN_H -#include "../psx/psx.h" -#include "common.h" +#include "../psx/psx.h" +#include "common.h" +#include "config.h" #include @@ -22,29 +23,32 @@ typedef struct { unsigned int width, height, scale; unsigned int image_width, image_height; unsigned int image_xoff, image_yoff; - unsigned int format; - unsigned int texture_width, texture_height; - - int bilinear; - int fullscreen; - int vertical_mode; - int debug_mode; - int open; + unsigned int format; + unsigned int texture_width, texture_height; + + void* external_window; + int texture_scale_mode; + int bilinear; + int fullscreen; + int vertical_mode; + int debug_mode; + int open; SDL_GameController* controller; } psxe_screen_t; -psxe_screen_t* psxe_screen_create(void); -void psxe_screen_init(psxe_screen_t*, psx_t*); -void psxe_screen_reload(psxe_screen_t*); -int psxe_screen_is_open(psxe_screen_t*); -void psxe_screen_update(psxe_screen_t*); -void psxe_screen_destroy(psxe_screen_t*); -void psxe_screen_set_scale(psxe_screen_t*, unsigned int); -void psxe_screen_toggle_debug_mode(psxe_screen_t*); +psxe_screen_t* psxe_screen_create(void); +void psxe_screen_init(psxe_screen_t*, psx_t*, const psxe_config_t*); +void psxe_screen_reload(psxe_screen_t*); +int psxe_screen_is_open(psxe_screen_t*); +void psxe_screen_update(psxe_screen_t*); +void psxe_screen_destroy(psxe_screen_t*); +void psxe_screen_set_scale(psxe_screen_t*, unsigned int); +void psxe_screen_set_window_handle(psxe_screen_t*, void* native_handle); +void psxe_screen_toggle_debug_mode(psxe_screen_t*); // GPU event handlers void psxe_gpu_dmode_event_cb(psx_gpu_t*); void psxe_gpu_vblank_event_cb(psx_gpu_t*); -#endif \ No newline at end of file +#endif diff --git a/psx/dev/bios.c b/psx/dev/bios.c index deb9345..5258c1b 100644 --- a/psx/dev/bios.c +++ b/psx/dev/bios.c @@ -1,13 +1,14 @@ #include "bios.h" -#include "../log.h" - -#include -#include -#include - -psx_bios_t* psx_bios_create(void) { - return (psx_bios_t*)malloc(sizeof(psx_bios_t)); -} +#include "../log.h" + +#include +#include +#include +#include + +psx_bios_t* psx_bios_create(void) { + return (psx_bios_t*)malloc(sizeof(psx_bios_t)); +} void psx_bios_init(psx_bios_t* bios) { memset(bios, 0, sizeof(psx_bios_t)); @@ -17,14 +18,16 @@ void psx_bios_init(psx_bios_t* bios) { bios->bus_delay = 18; } -int psx_bios_load(psx_bios_t* bios, const char* path) { - if (!path) - return 0; - - FILE* file = fopen(path, "rb"); - - if (!file) - return 1; +int psx_bios_load(psx_bios_t* bios, const char* path) { + if (!path) + return 0; + + FILE* file = fopen(path, "rb"); + + if (!file) { + log_error("Failed to open BIOS at '%s': %s", path, strerror(errno)); + return 1; + } // Almost all PS1 BIOS ROMs are 512 KiB in size. // There's (at least) one exception, and that is SCPH-5903. @@ -40,8 +43,10 @@ int psx_bios_load(psx_bios_t* bios, const char* path) { bios->buf = malloc(size); bios->io_size = size; - if (!fread(bios->buf, 1, size, file)) - return 2; + if (!fread(bios->buf, 1, size, file)) { + log_error("Failed to read BIOS at '%s': %s", path, strerror(errno)); + return 2; + } fclose(file); @@ -75,4 +80,4 @@ void psx_bios_write8(psx_bios_t* bios, uint32_t offset, uint8_t value) { void psx_bios_destroy(psx_bios_t* bios) { free(bios->buf); free(bios); -} \ No newline at end of file +} diff --git a/psx/dev/cdrom/cdrom.c b/psx/dev/cdrom/cdrom.c index 5ddd259..61db4ad 100644 --- a/psx/dev/cdrom/cdrom.c +++ b/psx/dev/cdrom/cdrom.c @@ -4,6 +4,7 @@ #include #include "cdrom.h" +#include "../../log.h" typedef void (*cdrom_cmd_func)(psx_cdrom_t* cdrom); @@ -194,11 +195,21 @@ int psx_cdrom_open(psx_cdrom_t* cdrom, const char* path) { cdrom->disc_type = psx_disc_open(cdrom->disc, path); if (cdrom->disc_type == CDT_ERROR) { + log_error("Failed to open CD image: %s", path); psx_cdrom_close(cdrom); return 0; } + if (cdrom->disc_type != CDT_LICENSED) { + log_error("CD image not recognized as a licensed PSX disc (type=%d). Use a proper BIN/CUE rip.", cdrom->disc_type); + psx_cdrom_close(cdrom); + + return 0; + } + + log_info("CD image type: Licensed"); + return 1; } @@ -759,4 +770,4 @@ void psx_cdrom_write32(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value) { void psx_cdrom_write16(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value) { assert("16-bit CDROM writes are not supported" && 0); -} \ No newline at end of file +} diff --git a/psx/dev/cdrom/disc.c b/psx/dev/cdrom/disc.c index af5b5fd..2341c12 100644 --- a/psx/dev/cdrom/disc.c +++ b/psx/dev/cdrom/disc.c @@ -1,8 +1,10 @@ #include #include +#include #include "disc.h" #include "cue.h" +#include "../../log.h" #define MSF_TO_LBA(m, s, f) ((m * 4500) + (s * 75) + f) @@ -13,8 +15,67 @@ const char* disc_cd_extensions[] = { 0 }; +typedef struct { + FILE* file; + uint32_t sector_size; + uint32_t sector_count; +} raw_disc_t; + +static int raw_read(void* udata, uint32_t lba, void* buf) { + raw_disc_t* raw = (raw_disc_t*)udata; + + if (!raw || !raw->file) + return 0; + + if (lba >= raw->sector_count) + return 0; + + if (fseek(raw->file, (long long)lba * raw->sector_size, SEEK_SET)) + return 0; + + size_t to_read = raw->sector_size; + memset(buf, 0, CD_SECTOR_SIZE); + + return fread(buf, 1, to_read, raw->file) == to_read ? TS_DATA : 0; +} + +static int raw_query(void* udata, uint32_t lba) { + raw_disc_t* raw = (raw_disc_t*)udata; + + return (raw && (lba < raw->sector_count)) ? TS_DATA : TS_FAR; +} + +static int raw_get_track_number(void* udata, uint32_t lba) { + (void)udata; (void)lba; + return 1; +} + +static int raw_get_track_count(void* udata) { + (void)udata; + return 1; +} + +static uint32_t raw_get_track_lba(void* udata, int track) { + (void)udata; (void)track; + return 0; +} + +static void raw_destroy(void* udata) { + raw_disc_t* raw = (raw_disc_t*)udata; + + if (raw && raw->file) + fclose(raw->file); + + free(raw); +} + psx_disc_t* psx_disc_create(void) { - return malloc(sizeof(psx_disc_t)); + psx_disc_t* disc = malloc(sizeof(psx_disc_t)); + + if (disc) + memset(disc, 0, sizeof(psx_disc_t)); + + return disc; } int disc_get_extension(const char* path) { @@ -81,12 +142,49 @@ int psx_disc_open_as(psx_disc_t* disc, const char* path, int type) { if (cue_load(cue, LD_FILE)) return CDT_ERROR; } break; + + case CD_EXT_BIN: + case CD_EXT_ISO: { + FILE* f = fopen(path, "rb"); + + if (!f) + return CDT_ERROR; + + fseek(f, 0, SEEK_END); + long long size = ftell(f); + fseek(f, 0, SEEK_SET); + + raw_disc_t* raw = (raw_disc_t*)malloc(sizeof(raw_disc_t)); + + raw->file = f; + raw->sector_size = (type == CD_EXT_BIN) ? CD_SECTOR_SIZE : 2048; + raw->sector_count = (uint32_t)(size / raw->sector_size); + + disc->udata = raw; + disc->read_sector = raw_read; + disc->query_sector = raw_query; + disc->get_track_number = raw_get_track_number; + disc->get_track_count = raw_get_track_count; + disc->get_track_lba = raw_get_track_lba; + disc->destroy = raw_destroy; + } break; + + default: + return CDT_ERROR; + } + + if (!disc->read_sector) { + log_error("Unsupported or failed to open disc image: %s", path); + return CDT_ERROR; } return disc_get_cd_type(disc); } int psx_disc_read(psx_disc_t* disc, uint32_t lba, void* buf) { + if (!disc->read_sector) + return 0; + return disc->read_sector(disc->udata, lba, buf); } @@ -110,4 +208,4 @@ void psx_disc_destroy(psx_disc_t* disc) { disc->destroy(disc->udata); free(disc); -} \ No newline at end of file +}