Major improvements to the SDL debugging interface, especially on Windows

1. No more separate EXE for the debugger version, the console can be freely opened at any time with ^C, and closed freely without terminating the emulator
2. On all platforms, the SDL emulator window is no longer unresponsive while the debugger is waiting for an input
3. Pressing keys in the emulator window while the debugger is waiting for inputs will trigger a terminal bell
4. Async debugging now supports Windows 7, 8.x, and earlier builds of Windows 10
This commit is contained in:
Lior Halphon 2025-03-27 23:45:51 +02:00
parent 4e35048f46
commit 15215000f9
12 changed files with 431 additions and 160 deletions

View File

@ -272,10 +272,9 @@ endif
ifeq ($(PLATFORM),windows32)
CFLAGS += -IWindows -Drandom=rand --target=x86_64-pc-windows
LDFLAGS += -lmsvcrt -lkernel32 -lcomdlg32 -luser32 -lshell32 -lole32 -ladvapi32 -ldwmapi -lSDL2main -Wl,/MANIFESTFILE:NUL --target=x86_64-pc-windows -v
SDL_LDFLAGS := -lSDL2
GL_LDFLAGS := -lopengl32
LDFLAGS += -lmsvcrt -lkernel32 -Wl,/MANIFESTFILE:NUL --target=x86_64-pc-windows
SDL_LDFLAGS := -lSDL2 -lcomdlg32 -luser32 -lshell32 -lole32 -ladvapi32 -ldwmapi -lSDL2main
GL_LDFLAGS := -lopengl32
ifneq ($(REDIST_XAUDIO),)
CFLAGS += -DREDIST_XAUDIO
LDFLAGS += -lxaudio2_9redist
@ -326,7 +325,7 @@ LDFLAGS += -Wl,/NODEFAULTLIB:libcmt.lib
ifneq ($(USE_MSVCRT_DLL),)
CFLAGS += -D_NO_CRT_STDIO_INLINE -DUSE_MSVCRT_DLL
$(BIN)/SDL/sameboy.exe: $(OBJ)/Windows/msvcrt.lib
$(BIN)/SDL/sameboy_debugger.exe: $(OBJ)/Windows/msvcrt.lib
$(LIBDIR)/libsameboy.dll: $(OBJ)/Windows/msvcrt.lib
endif
endif
@ -378,7 +377,7 @@ endif
# Define our targets
ifeq ($(PLATFORM),windows32)
SDL_TARGET := $(BIN)/SDL/sameboy.exe $(BIN)/SDL/sameboy_debugger.exe $(BIN)/SDL/SDL2.dll
SDL_TARGET := $(BIN)/SDL/sameboy.exe $(BIN)/SDL/SDL2.dll $(BIN)/SDL/sameboy_debugger.txt
TESTER_TARGET := $(BIN)/tester/sameboy_tester.exe
else
SDL_TARGET := $(BIN)/SDL/sameboy
@ -452,15 +451,15 @@ endif
$(OBJ)/SDL/%.dep: SDL/%
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
$(CC) $(CFLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -MT $(OBJ)/$^.o -M $^ -o $@
$(OBJ)/OpenDialog/%.dep: OpenDialog/%
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
$(CC) $(CFLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -MT $(OBJ)/$^.o -M $^ -o $@
$(OBJ)/%.dep: %
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
$(CC) $(CFLAGS) -MT $(OBJ)/$^.o -M $^ -o $@
# Compilation rules
@ -661,14 +660,18 @@ ifeq ($(CONF), release)
$(CODESIGN) $@
endif
# Windows version builds two, one with a console and one without it
$(BIN)/SDL/sameboy.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS) -Wl,/subsystem:windows
$(BIN)/SDL/sameboy_debugger.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS) -Wl,/subsystem:console
$(BIN)/SDL/sameboy_debugger.txt:
echo Looking for sameboy_debugger.exe? > $@
echo\>> $@
echo Starting with SameBoy v1.0.1, sameboy.exe and sameboy_debugger.exe >> $@
echo have been merged into a single executable. You can open a debugger >> $@
echo console at any time by pressing Ctrl+C to interrupt the currently >> $@
echo open ROM. Once you're done debugging, you can close the debugger >> $@
echo console and resume normal execution. >> $@
ifneq ($(USE_WINDRES),)
$(OBJ)/%.o: %.rc
@ -894,7 +897,7 @@ $(OBJ)/exports.def: $(OBJ)/exports $(OBJ)/names
$(LIBDIR)/libsameboy.dll: $(CORE_OBJECTS) | $(OBJ)/exports.def
-@$(MKDIR) -p $(dir $@)
$(CC) $(LDFLAGS) -Wl,-lldmingw -Wl,/def:$(OBJ)/exports.def -shared $(CFLAGS) $^ -o $@
$(CC) $(LDFLAGS) -Wl,/def:$(OBJ)/exports.def -shared $(CFLAGS) $^ -o $@
# CPPP doesn't like multibyte characters, so we replace the single quote character before processing so it doesn't complain
$(INC)/%.h: Core/%.h

View File

@ -17,6 +17,7 @@
#define SGR(x) CSI(x "m")
static bool initialized = false;
static bool no_csi = false;
typedef struct listent_s listent_t;
struct listent_s {
@ -111,12 +112,12 @@ static bool is_term(void)
SetConsoleMode(stdin_handle, input_mode);
SetConsoleMode(stdout_handle, output_mode);
if (before.dwCursorPosition.X != after.dwCursorPosition.X ||
before.dwCursorPosition.Y != after.dwCursorPosition.Y) {
printf("\r \r");
return false;
no_csi = true;
}
return true;
#else
return getenv("TERM");
@ -158,10 +159,12 @@ static unsigned long input_mode, output_mode;
static void cleanup(void)
{
printf(CSI("!p")); // reset
if (!no_csi) {
printf(CSI("!p")); // reset
}
fflush(stdout);
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), input_mode);
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), output_mode);
fflush(stdout);
}
static bool initialize(void)
@ -179,10 +182,14 @@ static bool initialize(void)
GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &output_mode);
once = true;
}
if (no_csi) {
initialized = true;
return true;
}
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_VIRTUAL_TERMINAL_INPUT);
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
printf(CSI("%dB") "\n" CSI("A") ESC("7") CSI("B"), height);
fprintf(stdout, CSI("%dB") "\n" CSI("A") ESC("7") CSI("B"), height);
fflush(stdout);
initialized = true;
@ -210,7 +217,9 @@ static struct termios terminal;
static void cleanup(void)
{
printf(CSI("!p")); // reset
if (!no_csi) {
printf(CSI("!p")); // reset
}
tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminal);
fflush(stdout);
}
@ -264,6 +273,8 @@ static bool repeat_empty = false;
static bool redraw_prompt(bool force)
{
if (no_csi) return true;
if (line.reverse_search) {
if (!force) return false;
if (line.length == 0) {
@ -525,6 +536,7 @@ restart:
static pthread_mutex_t terminal_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t lines_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t lines_cond = PTHREAD_COND_INITIALIZER;
static void (*line_ready_callback)(void) = NULL;
static char reverse_search_mainloop(void)
{
@ -549,14 +561,6 @@ static char reverse_search_mainloop(void)
delete_word(false);
redraw_prompt(true);
break;
#ifndef _WIN32
case CTL('Z'):
set_line("");
raise(SIGSTOP);
initialize(); // Reinitialize
redraw_prompt(true);
break;
#endif
case CTL('H'):
case 0x7F: // Backspace
delete(1, false);
@ -583,6 +587,39 @@ static char reverse_search_mainloop(void)
}
}
static void no_csi_mainloop(void)
{
while (true) {
char *expression = NULL;
size_t size = 0;
errno = 0;
if (getline(&expression, &size, stdin) <= 0) {
if (!errno) {
continue;
}
return;
}
pthread_mutex_lock(&lines_lock);
if (!expression) {
add_entry(&lines, "");
}
else {
size_t length = strlen(expression);
if (expression[length - 1] == '\n') {
expression[length - 1] = 0;
}
add_entry(&lines, expression);
free(expression);
}
pthread_cond_signal(&lines_cond);
pthread_mutex_unlock(&lines_lock);
if (line_ready_callback) {
line_ready_callback();
}
}
}
static
@ -593,6 +630,11 @@ void *
#endif
mainloop(char *(*completer)(const char *substring, uintptr_t *context))
{
if (no_csi) {
no_csi_mainloop();
return 0;
}
listent_t *history_line = NULL;
uintptr_t complete_context = 0;
size_t completion_length = 0;
@ -614,6 +656,10 @@ mainloop(char *(*completer)(const char *substring, uintptr_t *context))
else {
c = raw_getc();
}
if (c == EOF) {
return 0;
}
pthread_mutex_lock(&terminal_lock);
switch (c) {
@ -649,6 +695,9 @@ mainloop(char *(*completer)(const char *substring, uintptr_t *context))
add_entry(&lines, CON_EOF);
pthread_cond_signal(&lines_cond);
pthread_mutex_unlock(&lines_lock);
if (line_ready_callback) {
line_ready_callback();
}
}
break;
case CTL('E'):
@ -695,14 +744,6 @@ mainloop(char *(*completer)(const char *substring, uintptr_t *context))
delete_word(false);
complete_context = completion_length = 0;
break;
#ifndef _WIN32
case CTL('Z'):
set_line("");
complete_context = completion_length = 0;
raise(SIGSTOP);
initialize(); // Reinitialize
break;
#endif
case '\r':
case '\n':
pthread_mutex_lock(&lines_lock);
@ -714,6 +755,9 @@ mainloop(char *(*completer)(const char *substring, uintptr_t *context))
}
pthread_cond_signal(&lines_cond);
pthread_mutex_unlock(&lines_lock);
if (line_ready_callback) {
line_ready_callback();
}
if (line.length) {
listent_t *dup = reverse_find(history.last, line.content, true);
if (dup) {
@ -811,22 +855,24 @@ mainloop(char *(*completer)(const char *substring, uintptr_t *context))
}
break;
case '\t': {
char temp = line.content[line.position - completion_length];
line.content[line.position - completion_length] = 0;
char *completion = completer? completer(line.content, &complete_context) : NULL;
line.content[line.position - completion_length] = temp;
if (completion) {
if (completion_length) {
delete(completion_length, false);
if (!no_csi) {
char temp = line.content[line.position - completion_length];
line.content[line.position - completion_length] = 0;
char *completion = completer? completer(line.content, &complete_context) : NULL;
line.content[line.position - completion_length] = temp;
if (completion) {
if (completion_length) {
delete(completion_length, false);
}
insert(completion);
completion_length = strlen(completion);
free(completion);
}
insert(completion);
completion_length = strlen(completion);
free(completion);
else {
printf("\a");
}
break;
}
else {
printf("\a");
}
break;
}
default:
if (c >= ' ') {
@ -845,6 +891,12 @@ mainloop(char *(*completer)(const char *substring, uintptr_t *context))
return 0;
}
void CON_set_line_ready_callback(void (*callback)(void))
{
line_ready_callback = callback;
}
char *CON_readline(const char *new_prompt)
{
pthread_mutex_lock(&terminal_lock);
@ -896,8 +948,8 @@ bool CON_start(char *(*completer)(const char *substring, uintptr_t *context))
void CON_attributed_print(const char *string, CON_attributes_t *attributes)
{
if (!initialized) {
printf("%s", string);
if (!initialized || no_csi) {
fprintf(stdout, "%s", string);
return;
}
static bool pending_newline = false;
@ -1021,3 +1073,8 @@ void CON_set_repeat_empty(bool repeat)
{
repeat_empty = repeat;
}
bool CON_no_csi_mode(void)
{
return no_csi;
}

View File

@ -47,3 +47,5 @@ void CON_printf(const char *fmt, ...) __printflike(1, 2);
void CON_attributed_printf(const char *fmt, CON_attributes_t *attributes,...) __printflike(1, 3);
void CON_set_async_prompt(const char *string);
void CON_set_repeat_empty(bool repeat);
void CON_set_line_ready_callback(void (*callback)(void));
bool CON_no_csi_mode(void);

View File

@ -14,7 +14,7 @@
#ifdef _WIN32
#include <dwmapi.h>
#include <associations.h>
#include "windows_associations.h"
#include <SDL_syswm.h>
#endif

View File

@ -34,6 +34,10 @@ enum pending_command {
GB_SDL_QUIT_COMMAND,
GB_SDL_LOAD_STATE_FROM_FILE_COMMAND,
GB_SDL_CART_SWAP_COMMAND,
GB_SDL_DEBUGGER_INTERRUPT_COMMAND,
#ifdef _WIN32
GB_SDL_HIDE_DEBUGGER_COMMAND,
#endif
};
#define GB_SDL_DEFAULT_SCALE_MAX 8

View File

@ -13,12 +13,11 @@
#include "shader.h"
#include "audio/audio.h"
#include "console.h"
#ifndef _WIN32
#include <fcntl.h>
#else
#ifdef _WIN32
#include <Windows.h>
#include <associations.h>
#include "windows_associations.h"
#endif
static bool stop_on_start = false;
@ -42,6 +41,15 @@ bool uses_gl(void)
return gl_context;
}
void rerender_screen(void)
{
render_texture(active_pixel_buffer, configuration.blending_mode? previous_pixel_buffer : NULL);
#ifdef _WIN32
/* Required for some Windows 10 machines, god knows why */
render_texture(active_pixel_buffer, configuration.blending_mode? previous_pixel_buffer : NULL);
#endif
}
void set_filename(const char *new_filename, typeof(free) *new_free_function)
{
if (filename && free_function) {
@ -93,22 +101,74 @@ static void handle_eof(void)
static char *input_callback(GB_gameboy_t *gb)
{
if (CON_no_csi_mode()) {
fprintf(stdout, "> ");
fflush(stdout);
}
#ifdef _WIN32
DWORD pid;
GetWindowThreadProcessId(GetForegroundWindow(), &pid);
if (pid == GetCurrentProcessId()) {
BringWindowToTop(GetConsoleWindow());
}
#endif
retry: {
char *ret = CON_readline("Stopped> ");
if (strcmp(ret, CON_EOF) == 0) {
handle_eof();
free(ret);
CON_set_async_prompt("Stopped> ");
char *ret = CON_readline_async();
if (!ret) {
#ifdef _WIN32
HWND window = GetConsoleWindow();
if (pending_command == GB_SDL_HIDE_DEBUGGER_COMMAND || !window) return strdup("c");
ShowWindow(window, SW_SHOW);
#endif
SDL_Event event;
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_DISPLAYEVENT:
update_swap_interval();
break;
case SDL_WINDOWEVENT: {
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
update_viewport();
}
if (event.window.type == SDL_WINDOWEVENT_MOVED
#if SDL_COMPILEDVERSION > 2018
|| event.window.type == SDL_WINDOWEVENT_DISPLAY_CHANGED
#endif
) {
update_swap_interval();
}
rerender_screen();
break;
}
case SDL_QUIT:
pending_command = GB_SDL_QUIT_COMMAND;
return strdup("c");
case SDL_KEYDOWN:
fputc('\a', stdout);
fflush(stdout);
break;
default:
break;
}
goto retry;
}
else {
if (strcmp(ret, CON_EOF) == 0) {
free(ret);
handle_eof();
goto retry;
}
else if (!CON_no_csi_mode()) {
CON_attributes_t attributes = {.bold = true};
CON_attributed_printf("> %s\n", &attributes, ret);
}
CON_set_async_prompt("> ");
return ret;
}
}
static char *asyc_input_callback(GB_gameboy_t *gb)
static char *async_input_callback(GB_gameboy_t *gb)
{
retry: {
char *ret = CON_readline_async();
@ -187,6 +247,7 @@ static void open_menu(void)
}
size_t previous_width = GB_get_screen_width(&gb);
run_gui(true);
rerender_screen();
SDL_ShowCursor(SDL_DISABLE);
if (audio_playing) {
GB_audio_set_paused(false);
@ -204,6 +265,24 @@ static void open_menu(void)
}
}
static void console_line_ready(void)
{
static SDL_Event event = {
.type = SDL_USEREVENT
};
SDL_PushEvent(&event);
}
static void configure_console(void)
{
CON_set_async_prompt("> ");
CON_set_repeat_empty(true);
CON_set_line_ready_callback(console_line_ready);
GB_set_log_callback(&gb, log_callback);
GB_set_input_callback(&gb, input_callback);
GB_set_async_input_callback(&gb, async_input_callback);
}
static void handle_events(GB_gameboy_t *gb)
{
SDL_Event event;
@ -409,8 +488,7 @@ static void handle_events(GB_gameboy_t *gb)
}
case SDL_SCANCODE_C:
if (event.type == SDL_KEYDOWN && (event.key.keysym.mod & KMOD_CTRL)) {
CON_print("^C\a\n");
GB_debugger_break(gb);
pending_command = GB_SDL_DEBUGGER_INTERRUPT_COMMAND;
}
break;
@ -594,16 +672,7 @@ static void rumble(GB_gameboy_t *gb, double amp)
static void debugger_interrupt(int ignore)
{
if (!GB_is_inited(&gb)) exit(0);
/* ^C twice to exit */
if (GB_debugger_is_stopped(&gb)) {
GB_save_battery(&gb, battery_save_path_ptr);
exit(0);
}
if (console_supported) {
CON_print("^C\n");
}
GB_debugger_break(&gb);
pending_command = GB_SDL_DEBUGGER_INTERRUPT_COMMAND;
}
#ifndef _WIN32
@ -638,6 +707,75 @@ static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
GB_audio_queue_sample(sample);
}
#ifdef _WIN32
static BOOL windows_console_handler(DWORD signal)
{
/*
Hack: prevents process termination on console close
https://twitter.com/yo_yo_yo_jbo/status/1904592584326218069
Thanks JBO!
*/
if (signal == CTRL_C_EVENT) {
/* Only happens in no-csi mode */
pending_command = GB_SDL_DEBUGGER_INTERRUPT_COMMAND;
TerminateThread(GetCurrentThread(), 0);
}
pending_command = GB_SDL_HIDE_DEBUGGER_COMMAND;
console_line_ready();
TerminateThread(GetCurrentThread(), 0);
return false;
}
static void initialize_windows_console(void)
{
if (AllocConsole()) {
SetConsoleTitle("SameBoy Debugger Console");
freopen("CONIN$", "r", stdin);
setvbuf(stdin, NULL, _IONBF, 0);
freopen("CONOUT$", "w", stdout);
setvbuf(stdout, NULL, _IONBF, 0);
freopen("CONOUT$", "w", stderr);
setvbuf(stderr, NULL, _IONBF, 0);
console_supported = CON_start(completer);
if (console_supported) {
configure_console();
}
/* I would set a callback via SetConsoleCtrlHandler, but the function (CtrlRoutine) that
eventually calls our callback takes a lock and doesn't release it (as it expects the
process to exit afterwards). The solution is to take a more violent approach and hook
it instead. */
#if defined(__x86_64__) || defined(__i386__)
uint8_t *patch_address = (void *)(GetProcAddress(GetModuleHandleA("KernelBase.dll"), "CtrlRoutine") ?:
GetProcAddress(GetModuleHandleA("Kernel32.dll"), "CtrlRoutine"));
#else
uint8_t *patch_address = NULL;
#endif
if (!patch_address) {
EnableMenuItem(GetSystemMenu(GetConsoleWindow(), false), SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
}
else {
DWORD old_protection;
VirtualProtect(patch_address, 0x20, PAGE_READWRITE, &old_protection);
if (sizeof(&windows_console_handler) == 8) {
*(patch_address++) = 0x48; // movabs
}
*(patch_address++) = 0xb8; // mov
(*(uintptr_t *)patch_address) = (uintptr_t)&windows_console_handler;
patch_address += sizeof(&windows_console_handler);
// jmp rax/eax
*(patch_address++) = 0xff;
*(patch_address++) = 0xe0;
VirtualProtect(patch_address, 0x20, old_protection, &old_protection);
}
}
}
#endif
static bool doing_hot_swap = false;
static bool handle_pending_command(void)
@ -705,6 +843,36 @@ static bool handle_pending_command(void)
case GB_SDL_QUIT_COMMAND:
GB_save_battery(&gb, battery_save_path_ptr);
exit(0);
case GB_SDL_DEBUGGER_INTERRUPT_COMMAND:
if (!GB_is_inited(&gb)) exit(0);
#ifdef _WIN32
initialize_windows_console();
#endif
/* ^C twice to exit */
if (GB_debugger_is_stopped(&gb)) {
#ifndef _WIN32
GB_save_battery(&gb, battery_save_path_ptr);
exit(0);
#else
break;
#endif
}
if (console_supported) {
CON_print("^C\n");
}
GB_debugger_break(&gb);
break;
#if _WIN32
case GB_SDL_HIDE_DEBUGGER_COMMAND:
HWND console_window = GetConsoleWindow();
ShowWindow(console_window, SW_HIDE);
FreeConsole();
SDL_RaiseWindow(window);
break;
#endif
}
return false;
}
@ -869,10 +1037,7 @@ restart:;
GB_apu_set_sample_callback(&gb, gb_audio_callback);
if (console_supported) {
CON_set_async_prompt("> ");
GB_set_log_callback(&gb, log_callback);
GB_set_input_callback(&gb, input_callback);
GB_set_async_input_callback(&gb, asyc_input_callback);
configure_console();
}
GB_debugger_set_reload_callback(&gb, debugger_reload_callback);
@ -1106,6 +1271,16 @@ static void handle_model_option(const char *model_string)
}
}
#ifdef _WIN32
/* raise is buggy and for some reason not always go through our signal handler, so
let's just place the implementation with a direct call to debugger_interrupt. */
int raise(int signal)
{
debugger_interrupt(signal);
return 0;
}
#endif
int main(int argc, char **argv)
{
#ifdef _WIN32

View File

@ -1,7 +1,8 @@
#ifdef _WIN32
#include <windows.h>
#include <shlobj.h>
#include <stdbool.h>
#include "associations.h"
#include "windows_associations.h"
static bool set_registry_string(HKEY hive, const char *folder, const char *name, const char *value)
{
@ -88,3 +89,4 @@ bool GB_do_windows_association(void)
return ret;
}
#endif

5
SDL/windows_associations.h Executable file
View File

@ -0,0 +1,5 @@
#ifndef _WIN32
#error windows_associations.h included while building for a different platform
#endif
#include <stdbool.h>
bool GB_do_windows_association(void);

View File

@ -1,2 +0,0 @@
#include <stdbool.h>
bool GB_do_windows_association(void);

View File

@ -80,6 +80,11 @@ int _seh_filter_exe(unsigned exception_num, void *exception)
return 0;
}
int _seh_filter_dll(unsigned ExceptionNum, struct _EXCEPTION_POINTERS *ExceptionPtr)
{
return 0;
}
void _set_app_type(unsigned type)
{
}
@ -88,4 +93,13 @@ int _set_new_mode(int new_mode)
{
return 0;
}
#endif
int _execute_onexit_table(void *Table)
{
return 0;
}
void __std_type_info_destroy_list(void *const root_node)
{
}
#endif

View File

@ -11,4 +11,86 @@ FILE *__acrt_iob_func(unsigned index)
return (files[index] = fdopen(index, index == STDIN_FILENO? "r" : "w"));
}
#endif
#endif
#ifndef __MINGW32__
#ifndef __LIBRETRO__
int vasprintf(char **str, const char *fmt, va_list args)
{
size_t size = _vscprintf(fmt, args) + 1;
*str = (char*)malloc(size);
int ret = vsprintf(*str, fmt, args);
if (ret != size - 1) {
free(*str);
*str = NULL;
return -1;
}
return ret;
}
int asprintf(char **strp, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
int r = vasprintf(strp, fmt, args);
va_end(args);
return r;
}
#endif
#endif
/* This code is public domain -- Will Hartung 4/9/09 */
intptr_t getline(char **lineptr, size_t *n, FILE *stream)
{
char *bufptr = NULL;
char *p = bufptr;
size_t size;
int c;
if (lineptr == NULL) {
return -1;
}
if (stream == NULL) {
return -1;
}
if (n == NULL) {
return -1;
}
bufptr = *lineptr;
size = *n;
errno = 0;
c = fgetc(stream);
if (c == EOF) {
return -1;
}
if (bufptr == NULL) {
bufptr = (char*)malloc(128);
if (bufptr == NULL) {
return -1;
}
size = 128;
}
p = bufptr;
while (c != EOF) {
if ((p - bufptr) > (size - 1)) {
size = size + 128;
bufptr = (char*)realloc(bufptr, size);
if (bufptr == NULL) {
return -1;
}
}
*p++ = c;
if (c == '\n') {
break;
}
c = fgetc(stream);
}
*p++ = '\0';
*lineptr = bufptr;
*n = size;
return p - bufptr - 1;
}

View File

@ -21,83 +21,12 @@ int access(const char *filename, int mode);
#ifndef __MINGW32__
#ifndef __LIBRETRO__
static inline int vasprintf(char **str, const char *fmt, va_list args)
{
size_t size = _vscprintf(fmt, args) + 1;
*str = (char*)malloc(size);
int ret = vsprintf(*str, fmt, args);
if (ret != size - 1) {
free(*str);
*str = NULL;
return -1;
}
return ret;
}
static inline int asprintf(char **strp, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
int r = vasprintf(strp, fmt, args);
va_end(args);
return r;
}
int vasprintf(char **str, const char *fmt, va_list args);
int asprintf(char **strp, const char *fmt, ...);
#endif
#endif
/* This code is public domain -- Will Hartung 4/9/09 */
static inline size_t getline(char **lineptr, size_t *n, FILE *stream)
{
char *bufptr = NULL;
char *p = bufptr;
size_t size;
int c;
if (lineptr == NULL) {
return -1;
}
if (stream == NULL) {
return -1;
}
if (n == NULL) {
return -1;
}
bufptr = *lineptr;
size = *n;
c = fgetc(stream);
if (c == EOF) {
return -1;
}
if (bufptr == NULL) {
bufptr = (char*)malloc(128);
if (bufptr == NULL) {
return -1;
}
size = 128;
}
p = bufptr;
while (c != EOF) {
if ((p - bufptr) > (size - 1)) {
size = size + 128;
bufptr = (char*)realloc(bufptr, size);
if (bufptr == NULL) {
return -1;
}
}
*p++ = c;
if (c == '\n') {
break;
}
c = fgetc(stream);
}
*p++ = '\0';
*lineptr = bufptr;
*n = size;
return p - bufptr - 1;
}
intptr_t getline(char **lineptr, size_t *n, FILE *stream);
#define snprintf _snprintf
#define printf(...) fprintf(stdout, __VA_ARGS__)