diff options
-rw-r--r-- | Makefile | 43 | ||||
-rw-r--r-- | core.c | 86 | ||||
-rw-r--r-- | core.h | 8 | ||||
-rw-r--r-- | main.c | 74 | ||||
-rw-r--r-- | main.h | 2 | ||||
-rw-r--r-- | menu.c | 253 | ||||
-rw-r--r-- | menu.h | 2 | ||||
-rw-r--r-- | plat.h | 1 | ||||
-rw-r--r-- | plat_sdl.c | 8 | ||||
-rw-r--r-- | util.c | 53 | ||||
-rw-r--r-- | util.h | 8 |
11 files changed, 440 insertions, 98 deletions
@@ -40,7 +40,7 @@ pcsx_rearmed_MAKEFILE = Makefile.libretro ifeq ($(platform), trimui) OBJS += plat_trimui.o - CFLAGS += -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC + CFLAGS += -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC -DCONTENT_DIR='"/mnt/SDCARD/Roms"' LDFLAGS += -fno-PIC else ifeq ($(platform), unix) @@ -66,7 +66,7 @@ else ifeq ($(PROFILE), GENERATE) CFLAGS += -fprofile-generate=./profile/picoarch LDFLAGS += -lgcov else ifeq ($(PROFILE), APPLY) - CFLAGS += -fprofile-use -fprofile-dir=./profile/picoarch -fbranch-probabilities + CFLAGS += -fprofile-use -fprofile-dir=./profile/picoarch -fbranch-probabilities # -Wno-error=coverage-mismatch endif ifeq ($(MINUI), 1) @@ -192,9 +192,14 @@ snes9x2005_PAK_NAME = Super Nintendo (2005) dist-gmenu-section: mkdir -p pkg/gmenunx/Apps/picoarch + mkdir -p pkg/gmenunx/Apps/gmenunx/sections/emulators mkdir -p pkg/gmenunx/Apps/gmenunx/sections/libretro touch pkg/gmenunx/Apps/gmenunx/sections/libretro/.section +dist-gmenu-picoarch: $(BIN) dist-gmenu-section + cp $(BIN) pkg/gmenunx/Apps/picoarch + $(file >pkg/gmenunx/Apps/gmenunx/sections/emulators/picoarch,$(picoarch_SHORTCUT)) + define CORE_gmenushortcut = $1_NAME ?= $1 @@ -207,15 +212,20 @@ selectordir=/mnt/SDCARD/Roms/$($1_ROM_DIR) selectorfilter=$($1_TYPES) endef -dist-gmenu-$(1): $(BIN) $(1)_libretro.so dist-gmenu-section - cp $(BIN) $1_libretro.so pkg/gmenunx/Apps/picoarch +dist-gmenu-$(1): $(BIN) $(1)_libretro.so dist-gmenu-picoarch dist-gmenu-section + cp $1_libretro.so pkg/gmenunx/Apps/picoarch $$(file >pkg/gmenunx/Apps/gmenunx/sections/libretro/$(1),$$($(1)_SHORTCUT)) endef $(foreach core, $(CORES),$(eval $(call CORE_gmenushortcut,$(core)))) -dist-gmenu: $(foreach core, $(CORES), dist-gmenu-$(core)) +define picoarch_SHORTCUT +title=$(BIN) +exec=/mnt/SDCARD/Apps/picoarch/picoarch +endef + +dist-gmenu: $(foreach core, $(CORES), dist-gmenu-$(core)) dist-gmenu-picoarch # -- MinUI @@ -246,9 +256,30 @@ dist-minui-$(1): $(BIN) $(1)_libretro.so endef +define picoarch_LAUNCH_SH +#!/bin/sh +# picoarch.pak/launch.sh + +EMU_EXE=picoarch +EMU_DIR=$$(dirname "$$0") +EMU_NAME=$$EMU_EXE + +needs-swap + +HOME="/mnt/SDCARD/Games/picoarch.pak/" +cd "$$EMU_DIR" +"$$EMU_DIR/$$EMU_EXE" &> "/mnt/SDCARD/.minui/logs/$$EMU_NAME.txt" +endef + +dist-minui-picoarch: $(BIN) cores + mkdir -p "pkg/MinUI/Games/picoarch.pak" + $(file >picoarch_launch.sh,$(picoarch_LAUNCH_SH)) + mv picoarch_launch.sh "pkg/MinUI/Games/picoarch.pak/launch.sh" + cp $(BIN) $(SOFILES) "pkg/MinUI/Games/picoarch.pak" + $(foreach core, $(CORES),$(eval $(call CORE_pak_template,$(core)))) -dist-minui: $(foreach core, $(CORES), dist-minui-$(core)) +dist-minui: $(foreach core, $(CORES), dist-minui-$(core)) dist-minui-picoarch endif # MINUI=1 @@ -15,15 +15,17 @@ #include "unzip.h" #include "util.h" -#define PATH_SEPARATOR_CHAR '/' - struct core_cbs current_core; +char core_path[MAX_PATH]; +char content_path[MAX_PATH]; +static struct string_list *extensions; double sample_rate; double frame_rate; double aspect_ratio; unsigned audio_buffer_size_override; int state_slot; +int resume_slot = -1; static char config_dir[MAX_PATH]; static char save_dir[MAX_PATH]; @@ -33,29 +35,22 @@ static struct retro_disk_control_ext_callback disk_control_ext; static uint32_t buttons = 0; -#define MAX_EXTENSIONS 24 - -static void core_handle_zip(const char *path, struct retro_system_info *info, struct retro_game_info *game_info, FILE** file) { - const char *extensions[MAX_EXTENSIONS] = {0}; - char *extensionstr = strdup(info->valid_extensions); - char *ext = NULL; - char *saveptr = NULL; +static void core_handle_zip(const char *path, struct retro_game_info *game_info, FILE** file) { + const char *ext = NULL; int index = 0; bool haszip = false; FILE *dest = NULL; - if (info->valid_extensions && has_suffix_i(path, ".zip")) { - while((ext = strtok_r(index == 0 ? extensionstr : NULL, "|", &saveptr))) { + if (extensions && has_suffix_i(path, ".zip")) { + while((ext = extensions->list[index++])) { if (!strcmp(ext, "zip")) { haszip = true; break; } - extensions[index++] = ext; - if (index > MAX_EXTENSIONS - 1) break; } if (!haszip) { - if (!unzip_tmp(*file, extensions, temp_rom, MAX_PATH)) { + if (!unzip_tmp(*file, extensions->list, temp_rom, MAX_PATH)) { game_info->path = temp_rom; dest = fopen(temp_rom, "r"); if (dest) { @@ -65,7 +60,6 @@ static void core_handle_zip(const char *path, struct retro_system_info *info, st } } } - free(extensionstr); } static int core_load_game_info(const char *path, struct retro_game_info *game_info) { @@ -83,7 +77,7 @@ static int core_load_game_info(const char *path, struct retro_game_info *game_in current_core.retro_get_system_info(&info); - core_handle_zip(path, &info, game_info, &file); + core_handle_zip(path, game_info, &file); fseek(file, 0, SEEK_END); game_info->size = ftell(file); @@ -138,7 +132,7 @@ static void gamepak_related_name(char *buf, size_t len, const char *new_extensio void config_file_name(char *buf, size_t len, int is_game) { - if (is_game && content_path) { + if (is_game && content_path[0]) { gamepak_related_name(buf, len, ".cfg"); } else { snprintf(buf, len, "%s%s", config_dir, "picoarch.cfg"); @@ -307,6 +301,16 @@ error: return ret; } +int state_resume(void) { + int ret = 0; + if (resume_slot != -1) { + state_slot = resume_slot; + ret = state_read(); + resume_slot = -1; + } + return ret; +} + unsigned disc_get_count(void) { if (disk_control_ext.get_num_images) return disk_control_ext.get_num_images(); @@ -585,7 +589,25 @@ static int16_t pa_input_state(unsigned port, unsigned device, unsigned index, un return 0; } +void core_extract_name(const char* core_file, char *buf, size_t len) { + char *suffix = NULL; + + strncpy(buf, basename(core_file), MAX_PATH); + buf[len - 1] = 0; + + suffix = strrchr(buf, '_'); + if (suffix && !strcmp(suffix, "_libretro.so")) + *suffix = 0; + else { + suffix = strrchr(buf, '.'); + if (suffix && !strcmp(suffix, ".so")) + *suffix = 0; + } +} + int core_load(const char *corefile) { + struct retro_system_info info = {0}; + void (*set_environment)(retro_environment_t) = NULL; void (*set_video_refresh)(retro_video_refresh_t) = NULL; void (*set_audio_sample)(retro_audio_sample_t) = NULL; @@ -640,6 +662,11 @@ int core_load(const char *corefile) { set_input_state(pa_input_state); current_core.retro_init(); + + current_core.retro_get_system_info(&info); + if (info.valid_extensions) + extensions = string_split(info.valid_extensions, '|'); + current_core.initialized = true; PA_INFO("Finished loading core\n"); return 0; @@ -682,18 +709,35 @@ finish: return ret; } +void core_unload_content(void) { + if (temp_rom[0]) { + remove(temp_rom); + temp_rom[0] = '\0'; + } + content_path[0] = '\0'; +} + +const char **core_extensions(void) { + if (extensions) + return extensions->list; + + return NULL; +} + void core_unload(void) { PA_INFO("Unloading core...\n"); + core_unload_content(); + + string_list_free(extensions); + extensions = NULL; + if (current_core.initialized) { + sram_write(); current_core.retro_deinit(); current_core.initialized = false; } - if (temp_rom[0]) { - remove(temp_rom); - temp_rom[0] = '\0'; - } options_free(); if (current_core.handle) { @@ -2,6 +2,7 @@ #define _CORE_H__ #include "libretro.h" +#include "main.h" struct core_cbs { bool initialized; @@ -29,12 +30,15 @@ struct core_cbs { }; extern struct core_cbs current_core; +extern char core_path[MAX_PATH]; +extern char content_path[MAX_PATH]; extern double sample_rate; extern double frame_rate; extern double aspect_ratio; extern unsigned audio_buffer_size_override; extern int state_slot; +extern int resume_slot; void config_file_name(char *buf, size_t len, int is_game); void save_relative_path(char *buf, size_t len, const char *basename); @@ -46,14 +50,18 @@ bool state_allowed(void); void state_file_name(char *name, size_t size, int slot); int state_read(void); int state_write(void); +int state_resume(void); unsigned disc_get_count(void); unsigned disc_get_index(void); bool disc_switch_index(unsigned index); bool disc_replace_index(unsigned index, const char *content_path); +void core_extract_name(const char* core_file, char *buf, size_t len); int core_load(const char *corefile); int core_load_content(const char *path); +void core_unload_content(void); +const char **core_extensions(void); void core_unload(void); #endif @@ -17,36 +17,18 @@ #include <mmenu.h> #include <SDL/SDL.h> void* mmenu = NULL; -static int resume_slot = -1; char save_template_path[MAX_PATH]; #endif bool should_quit = false; unsigned current_audio_buffer_size; char core_name[MAX_PATH]; -char* content_path; int config_override = 0; static int last_screenshot = 0; static uint32_t vsyncs; static uint32_t renders; -static void extract_core_name(const char* core_file) { - char *suffix = NULL; - - strncpy(core_name, basename(core_file), MAX_PATH); - core_name[sizeof(core_name) - 1] = 0; - - suffix = strrchr(core_name, '_'); - if (suffix && !strcmp(suffix, "_libretro.so")) - *suffix = 0; - else { - suffix = strrchr(core_name, '.'); - if (suffix && !strcmp(suffix, ".so")) - *suffix = 0; - } -} - static void toggle_fast_forward(int force_off) { static int frameskip_style_was; @@ -294,7 +276,7 @@ void load_config(void) } } -static void load_config_keys(void) +void load_config_keys(void) { char *config = NULL; int kcount = 0; @@ -518,35 +500,50 @@ static void adjust_audio(void) { } int main(int argc, char **argv) { - if (argc != 3) { - printf("Usage: picoarch LIBRETRO_CORE CONTENT"); - return -1; + if (argc > 1) { + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + printf("Usage: picoarch [libretro_core [content]]\n"); + return 0; + } } - extract_core_name(argv[1]); - content_path = argv[2]; - - set_defaults(); - - if (core_load(argv[1])) { + if (plat_init()) { quit(-1); } - if (core_load_content(argv[2])) { + if (menu_init()) { quit(-1); } - if (plat_init()) { + if (argc > 1 && argv[1]) { + strncpy(core_path, argv[1], sizeof(core_path) - 1); + } else { + if (menu_select_core()) + quit(-1); + } + + core_extract_name(core_path, core_name, sizeof(core_name)); + + set_defaults(); + + if (core_load(core_path)) { quit(-1); } - /* Must happen after initializing plat_input */ - load_config_keys(); + if (argc > 2 && argv[2]) { + strncpy(content_path, argv[2], sizeof(content_path) - 1); + } else { + if (menu_select_content()) + quit(-1); + } - if (menu_init()) { + if (core_load_content(content_path)) { quit(-1); } + load_config_keys(); + plat_reinit(); + #ifdef MMENU mmenu = dlopen("libmmenu.so", RTLD_LAZY); @@ -554,14 +551,10 @@ int main(int argc, char **argv) { ResumeSlot_t ResumeSlot = (ResumeSlot_t)dlsym(mmenu, "ResumeSlot"); if (ResumeSlot) resume_slot = ResumeSlot(); } - - if (state_allowed() && resume_slot!=-1) { - state_slot = resume_slot; - state_read(); - resume_slot = -1; - } #endif + state_resume(); + show_startup_message(); do { count_fps(); @@ -575,9 +568,6 @@ int main(int argc, char **argv) { } int quit(int code) { - if (current_core.initialized) - sram_write(); - menu_finish(); core_unload(); plat_finish(); @@ -21,7 +21,6 @@ typedef enum { extern bool should_quit; extern unsigned current_audio_buffer_size; extern char core_name[MAX_PATH]; -extern char* content_path; extern int config_override; #ifdef MMENU @@ -45,6 +44,7 @@ int screenshot(void); void set_defaults(void); int save_config(int is_game); void load_config(void); +void load_config_keys(void); int remove_config(int is_game); void handle_emu_action(emu_action action); @@ -10,10 +10,14 @@ static int drew_alt_bg = 0; +static char cores_path[MAX_PATH]; +static struct dirent **corelist = NULL; +static int corelist_len = 0; + #define MENU_ALIGN_LEFT 0 #define MENU_X2 0 -#define CORE_OPTIONS_PER_PAGE 11 +#define MENU_ITEMS_PER_PAGE 11 typedef enum { @@ -22,7 +26,9 @@ typedef enum MA_MAIN_SAVE_STATE, MA_MAIN_LOAD_STATE, MA_MAIN_DISC_CTRL, + MA_MAIN_CORE_SEL, MA_MAIN_CORE_OPTS, + MA_MAIN_CONTENT_SEL, MA_MAIN_RESET_GAME, MA_MAIN_CREDITS, MA_MAIN_EXIT, @@ -100,11 +106,6 @@ static unsigned short fname2color(const char *fname) #include "libpicofe/menu.c" -static const char *menu_loop_romsel(char *curr_path, int len, - const char **filter_exts, - int (*extra_filter)(struct dirent **namelist, int count, - const char *basedir)) __attribute__((unused)); - static void draw_menu_message(const char *msg, void (*draw_more)(void)) __attribute__((unused)); static const char *mgn_saveloadcfg(int id, int *offs) @@ -144,6 +145,202 @@ static void draw_src_bg(void) { menu_darken_bg(g_menubg_ptr, g_menubg_src_ptr, g_menubg_src_h * g_menubg_src_pp, 0); } +static int mh_set_core(int id, int keys) { + if (corelist && id < corelist_len) + snprintf(core_path, sizeof(core_path), "%s/%s", cores_path, corelist[id]->d_name); + + return 1; +} + +static int core_selector(const struct dirent *ent) { + return has_suffix_i(ent->d_name, "_libretro.so"); +} + +static int menu_loop_core_page(int offset, int keys) { + static int sel = 0; + menu_entry e_menu_cores[MENU_ITEMS_PER_PAGE + 2] = {0}; /* +2 for Next, NULL */ + size_t menu_idx = 0; + int i; + char names[MENU_ITEMS_PER_PAGE][MAX_PATH]; + + for (i = offset, menu_idx = 0; i < corelist_len && menu_idx < MENU_ITEMS_PER_PAGE; i++) { + menu_entry *option; + struct dirent *ent = corelist[i]; + option = &e_menu_cores[menu_idx]; + core_extract_name(ent->d_name, names[menu_idx], sizeof(names[menu_idx])); + + option->name = names[menu_idx]; + option->beh = MB_OPT_CUSTOM; + option->id = i; + option->enabled = 1; + option->selectable = 1; + option->handler = mh_set_core; + menu_idx++; + } + + if (i < corelist_len) { + menu_entry *option; + option = &e_menu_cores[menu_idx]; + option->name = "Next page"; + option->beh = MB_OPT_CUSTOM; + option->id = i; + option->enabled = 1; + option->selectable = 1; + option->handler = menu_loop_core_page; + } + + return me_loop(e_menu_cores, &sel); +} + +int menu_select_core(void) { + int ret = -1; + getcwd(cores_path, MAX_PATH); + + corelist_len = scandir(cores_path, &corelist, core_selector, alphasort); + if (!corelist_len) return -1; + + plat_video_menu_enter(1); + + if (menu_loop_core_page(0, 0) < 0) + goto finish; + + if (core_path[0] == '\0') + goto finish; + + ret = 0; +finish: + /* wait until menu, ok, back is released */ + while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK)) + ; + + plat_video_menu_leave(); + + if (corelist_len > 0) { + while (corelist_len--) + free(corelist[corelist_len]); + free(corelist); + corelist = NULL; + } + return ret; +} + +int hidden_file_filter(struct dirent **namelist, int count, const char *basedir) { + int newcount = 0; + + for (int i = 0; i < count; i++) { + if (namelist[i]->d_name[0] == '.' && namelist[i]->d_name[1] != '.') { + free(namelist[i]); + namelist[i] = NULL; + } + } + + for (int i = 0; i < count; i++) { + if (namelist[i] != NULL) + namelist[newcount++] = namelist[i]; + } + + return newcount; +} + +const char *select_content(void) { + const char *fname = NULL; + const char **extensions = core_extensions(); + const char **exts_with_zip = NULL; + int i = 0, size = 0; + + if (content_path[0] == '\0') { + if (getenv("CONTENT_DIR")) { + strncpy(content_path, getenv("CONTENT_DIR"), sizeof(content_path) - 1); +#ifdef CONTENT_DIR + } else { + strncpy(content_path, CONTENT_DIR, sizeof(content_path) - 1); +#else + } else if (getenv("HOME")) { + strncpy(content_path, getenv("HOME"), sizeof(content_path) - 1); +#endif + } + } + + if (extensions) { + for (size = 0; extensions[size]; size++) + ; + } + + exts_with_zip = calloc(size + 2, sizeof (char *)); /* add 2 for "zip", NULL */ + + if (exts_with_zip) { + for (i = 0; extensions[i]; i++) { + exts_with_zip[i] = extensions[i]; + } + exts_with_zip[i] = "zip"; + } else { + exts_with_zip = extensions; + } + + fname = menu_loop_romsel(content_path, sizeof(content_path), exts_with_zip, hidden_file_filter); + + if (exts_with_zip != extensions) + free(exts_with_zip); + + return fname; +} + +int menu_select_content(void) { + const char *fname = NULL; + int ret = -1; + + plat_video_menu_enter(1); + fname = select_content(); + if (!fname) + goto finish; + + strncpy(content_path, fname, sizeof(content_path) - 1); + set_defaults(); + load_config(); + if (g_autostateld_opt) + resume_slot = 0; + ret = 0; + +finish: + /* wait until menu, ok, back is released */ + while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK)) + ; + + plat_video_menu_leave(); + return ret; +} + +static int menu_loop_select_content(int id, int keys) { + const char *fname = select_content(); + + if (fname == NULL) + return -1; + + core_unload_content(); + strncpy(content_path, fname, sizeof(content_path) - 1); + + set_defaults(); + + if (core_load_content(fname)) { + quit(-1); + } + + load_config(); + + if (plat_reinit()) { + quit(-1); + } + + load_config_keys(); + + if (g_autostateld_opt) { + resume_slot = 0; + state_resume(); + } + + return 1; +} + static int menu_loop_disc(int id, int keys) { static int sel = 0; @@ -181,7 +378,7 @@ static int menu_loop_core_options_page(int offset, int keys) { return 0; } - for (i = offset, menu_idx = 0; i < core_options.len && menu_idx < CORE_OPTIONS_PER_PAGE; i++) { + for (i = offset, menu_idx = 0; i < core_options.len && menu_idx < MENU_ITEMS_PER_PAGE; i++) { struct core_option_entry *entry = &core_options.entries[i]; menu_entry *option; const char *key = entry->key; @@ -294,22 +491,6 @@ static int key_config_loop_wrap(int id, int keys) return 0; } -static menu_entry e_menu_keyconfig[] = -{ - mee_handler_id ("Player controls", MA_CTRL_PLAYER1, key_config_loop_wrap), - mee_handler_id ("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap), - mee_cust_nosave ("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg), - mee_cust_nosave ("Save game config", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg), - mee_end, -}; - -static int menu_loop_keyconfig(int id, int keys) -{ - static int sel = 0; - me_loop(e_menu_keyconfig, &sel); - return 0; -} - const char *config_label(int id, int *offs) { return config_override ? "Loaded: game config" : "Loaded: global config"; } @@ -335,6 +516,24 @@ static int menu_loop_config_options(int id, int keys) return 0; } +static menu_entry e_menu_options[] = +{ + mee_handler ("Audio and video", menu_loop_video_options), + mee_handler_id("Emulator options", MA_MAIN_CORE_OPTS, menu_loop_core_options), + mee_handler_id("Player controls", MA_CTRL_PLAYER1, key_config_loop_wrap), + mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap), + mee_handler ("Save config", menu_loop_config_options), + mee_end, +}; + +static int menu_loop_options(int id, int keys) +{ + static int sel = 0; + me_loop(e_menu_options, &sel); + + return 0; +} + static int main_menu_handler(int id, int keys) { switch (id) @@ -365,12 +564,10 @@ static menu_entry e_menu_main[] = mee_handler_id("Save state", MA_MAIN_SAVE_STATE, main_menu_handler), mee_handler_id("Load state", MA_MAIN_LOAD_STATE, main_menu_handler), mee_handler_id("Disc control", MA_MAIN_DISC_CTRL, menu_loop_disc), - mee_handler_id("Emulator options", MA_MAIN_CORE_OPTS, menu_loop_core_options), - mee_handler ("Audio and video", menu_loop_video_options), - mee_handler ("Controls", menu_loop_keyconfig), /* mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler), */ - mee_handler ("Save config", menu_loop_config_options), + mee_handler ("Options", menu_loop_options), mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler), + mee_handler_id("Load new game", MA_MAIN_CONTENT_SEL, menu_loop_select_content), mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler), mee_end, }; @@ -431,7 +628,7 @@ void menu_loop(void) me_enable(e_menu_main, MA_MAIN_LOAD_STATE, mmenu == NULL); } #endif - me_loop_d(e_menu_main, &sel, NULL, NULL); + me_loop(e_menu_main, &sel); /* wait until menu, ok, back is released */ while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK)) @@ -6,6 +6,8 @@ int menu_init(void); void menu_loop(void); +int menu_select_core(void); +int menu_select_content(void); void menu_begin(void); void menu_end(void); void menu_finish(void); @@ -11,6 +11,7 @@ struct audio_frame { #define HUD_LEN 41 int plat_init(void); +int plat_reinit(void); void plat_finish(void); void plat_minimize(void); @@ -413,6 +413,14 @@ int plat_init(void) return 0; } +int plat_reinit(void) +{ + audio.in_sample_rate = sample_rate; + plat_sound_resize_buffer(); + scale_update_scaler(); + return 0; +} + void plat_finish(void) { plat_sound_finish(); @@ -1,5 +1,58 @@ +#include <stdlib.h> +#include <string.h> #include "util.h" +struct string_list *string_split(const char *string, char delim) { + const char *loc = string; + int size = 2; /* rest of string, NULL terminator */ + int index = 0; + char delims[2] = {0}; + char *saveptr = NULL; + struct string_list *list = calloc(1, sizeof(struct string_list)); + + if (!list) + goto finish; + + list->source = strdup(string); + if (!list->source) + goto finish; + + while((loc = strchr(loc, delim))) { + size++; + while (*loc && *loc == delim) loc++; + } + + list->list = calloc(size, sizeof(char *)); + if (!list->list) + goto finish; + + delims[0] = delim; + + while((loc = strtok_r(index == 0 ? list->source : NULL, delims, &saveptr))) { + list->list[index++] = loc; + if (index >= size) + break; + } + +finish: + return list; +} + +void string_list_free(struct string_list *list) { + if (list) { + if (list->list) { + free(list->list); + list->list = NULL; + } + if (list->source) { + free(list->source); + list->source = NULL; + } + + free(list); + } +} + void string_truncate(char *string, size_t max_len) { size_t len = strlen(string) + 1; if (len <= max_len) return; @@ -9,12 +9,20 @@ #define array_size(x) (sizeof(x) / sizeof(x[0])) +struct string_list { + const char **list; + char *source; +}; + static inline bool has_suffix_i(const char *str, const char *suffix) { const char *p = strrchr(str, suffix[0]); if (!p) p = str; return !strcasecmp(p, suffix); } +struct string_list *string_split(const char *string, char delim); +void string_list_free(struct string_list *list); + void string_truncate(char *string, size_t max_len); void string_wrap(char *string, size_t max_len, size_t max_lines); |