summaryrefslogtreecommitdiff
path: root/libblastem.c
diff options
context:
space:
mode:
Diffstat (limited to 'libblastem.c')
-rw-r--r--libblastem.c609
1 files changed, 302 insertions, 307 deletions
diff --git a/libblastem.c b/libblastem.c
index 80bb33d..f23d3e3 100644
--- a/libblastem.c
+++ b/libblastem.c
@@ -10,6 +10,13 @@
#include "sms.h"
static retro_environment_t retro_environment;
+static retro_video_refresh_t retro_video_refresh;
+static retro_audio_sample_batch_t retro_audio_sample_batch;
+static retro_input_poll_t retro_input_poll;
+static retro_input_state_t retro_input_state;
+
+static bool libretro_supports_bitmasks = false;
+
RETRO_API void retro_set_environment(retro_environment_t re)
{
retro_environment = re;
@@ -42,53 +49,52 @@ RETRO_API void retro_set_environment(retro_environment_t re)
re(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, (void *)desc);
}
-static retro_video_refresh_t retro_video_refresh;
RETRO_API void retro_set_video_refresh(retro_video_refresh_t rvf)
{
retro_video_refresh = rvf;
}
-RETRO_API void retro_set_audio_sample(retro_audio_sample_t ras)
-{
-}
+RETRO_API void retro_set_audio_sample(retro_audio_sample_t ras) { }
-static retro_audio_sample_batch_t retro_audio_sample_batch;
RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t rasb)
{
retro_audio_sample_batch = rasb;
}
-static retro_input_poll_t retro_input_poll;
RETRO_API void retro_set_input_poll(retro_input_poll_t rip)
{
retro_input_poll = rip;
}
-static retro_input_state_t retro_input_state;
RETRO_API void retro_set_input_state(retro_input_state_t ris)
{
retro_input_state = ris;
}
-int headless = 0;
-int exit_after = 0;
-int z80_enabled = 1;
-char *save_filename;
-tern_node *config;
-uint8_t use_native_states = 1;
-system_header *current_system;
-system_media media;
+char *save_filename = NULL;
+tern_node *config = NULL;
+int headless = 0;
+int exit_after = 0;
+int z80_enabled = 1;
+uint8_t use_native_states = 1;
+system_header *current_system = NULL;
+static system_media current_media;
RETRO_API void retro_init(void)
{
- render_audio_initialized(RENDER_AUDIO_S16, 53693175 / (7 * 6 * 4), 2, 4, sizeof(int16_t));
+ render_audio_initialized(RENDER_AUDIO_S16,
+ 53693175 / (7 * 6 * 4), 2, 4, sizeof(int16_t));
+
+ libretro_supports_bitmasks = false;
+ if (retro_environment(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
+ libretro_supports_bitmasks = true;
}
RETRO_API void retro_deinit(void)
{
- if (current_system) {
+ if (current_system)
retro_unload_game();
- }
+ libretro_supports_bitmasks = false;
}
RETRO_API unsigned retro_api_version(void)
@@ -98,61 +104,77 @@ RETRO_API unsigned retro_api_version(void)
RETRO_API void retro_get_system_info(struct retro_system_info *info)
{
- info->library_name = "BlastEm";
- info->library_version = "0.6.3-pre"; //TODO: share this with blastem.c
+ info->library_name = "BlastEm";
+ info->library_version = "0.6.3-pre"; /* TODO: share this with blastem.c */
info->valid_extensions = "md|gen|sms|bin|rom";
- info->need_fullpath = 0;
- info->block_extract = 0;
+ info->need_fullpath = false;
+ info->block_extract = false;
}
static vid_std video_standard;
static uint32_t last_width, last_height;
static uint32_t overscan_top, overscan_bot, overscan_left, overscan_right;
+
static void update_overscan(void)
{
uint8_t overscan;
retro_environment(RETRO_ENVIRONMENT_GET_OVERSCAN, &overscan);
- if (overscan) {
- overscan_top = overscan_bot = overscan_left = overscan_right = 0;
- } else {
- if (video_standard == VID_NTSC) {
- overscan_top = 11;
- overscan_bot = 8;
- overscan_left = 13;
- overscan_right = 14;
- } else {
- overscan_top = 30;
- overscan_bot = 24;
- overscan_left = 13;
- overscan_right = 14;
- }
- }
+ if (overscan)
+ {
+ overscan_top = 0;
+ overscan_bot = 0;
+ overscan_left = 0;
+ overscan_right = 0;
+ }
+ else
+ {
+ if (video_standard == VID_NTSC)
+ {
+ overscan_top = 11;
+ overscan_bot = 8;
+ overscan_left = 13;
+ overscan_right = 14;
+ }
+ else
+ {
+ overscan_top = 30;
+ overscan_bot = 24;
+ overscan_left = 13;
+ overscan_right = 14;
+ }
+ }
}
static float get_aspect_ratio(void)
{
- float aspect_width = LINEBUF_SIZE - overscan_left - overscan_right;
- float aspect_height = (video_standard == VID_NTSC ? 243 : 294) - overscan_top - overscan_bot;
+ float aspect_width = LINEBUF_SIZE - overscan_left - overscan_right;
+ float aspect_height = (video_standard == VID_NTSC ? 243 : 294)
+ - overscan_top - overscan_bot;
return aspect_width / aspect_height;
}
static int32_t sample_rate;
+
RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info)
{
+ double master_clock, lines;
+
update_overscan();
- last_width = LINEBUF_SIZE;
- info->geometry.base_width = info->geometry.max_width = LINEBUF_SIZE - (overscan_left + overscan_right);
- info->geometry.base_height = (video_standard == VID_NTSC ? 243 : 294) - (overscan_top + overscan_bot);
- last_height = info->geometry.base_height;
- info->geometry.max_height = info->geometry.base_height * 2;
+ last_width = LINEBUF_SIZE;
+ info->geometry.base_width = info->geometry.max_width = LINEBUF_SIZE - (overscan_left + overscan_right);
+ info->geometry.base_height = (video_standard == VID_NTSC ? 243 : 294) - (overscan_top + overscan_bot);
+ last_height = info->geometry.base_height;
+ info->geometry.max_height = info->geometry.base_height * 2;
info->geometry.aspect_ratio = get_aspect_ratio();
- double master_clock = video_standard == VID_NTSC ? 53693175 : 53203395;
- double lines = video_standard == VID_NTSC ? 262 : 313;
- info->timing.fps = master_clock / (3420.0 * lines);
- info->timing.sample_rate = master_clock / (7 * 6 * 24); //sample rate of YM2612
- sample_rate = info->timing.sample_rate;
- render_audio_initialized(RENDER_AUDIO_S16, info->timing.sample_rate, 2, 4, sizeof(int16_t));
- //force adjustment of resampling parameters since target sample rate may have changed slightly
+ master_clock = video_standard == VID_NTSC ? 53693175 : 53203395;
+ lines = video_standard == VID_NTSC ? 262 : 313;
+ info->timing.fps = master_clock / (3420.0 * lines);
+ info->timing.sample_rate = master_clock / (7 * 6 * 24); //sample rate of YM2612
+ sample_rate = info->timing.sample_rate;
+ render_audio_initialized(RENDER_AUDIO_S16,
+ info->timing.sample_rate, 2, 4, sizeof(int16_t));
+ /* Force adjustment of resampling parameters
+ * since target sample rate may have changed slightly */
current_system->set_speed_percent(current_system, 100);
}
@@ -177,12 +199,13 @@ RETRO_API void retro_reset(void)
static uint8_t started;
RETRO_API void retro_run(void)
{
- if (started) {
+ if (started)
current_system->resume_context(current_system);
- } else {
+ else
+ {
current_system->start_context(current_system, NULL);
started = 1;
- }
+ }
}
/* Returns the amount of data the implementation requires to serialize
@@ -202,10 +225,11 @@ RETRO_API bool retro_serialize(void *data, size_t size)
{
size_t actual_size;
uint8_t *tmp = current_system->serialize(current_system, &actual_size);
- if (actual_size > size) {
- free(tmp);
- return 0;
- }
+ if (actual_size > size)
+ {
+ free(tmp);
+ return 0;
+ }
memcpy(data, tmp, actual_size);
free(tmp);
return 1;
@@ -217,38 +241,41 @@ RETRO_API bool retro_unserialize(const void *data, size_t size)
return 1;
}
-RETRO_API void retro_cheat_reset(void)
-{
-}
-
-RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code)
-{
-}
+RETRO_API void retro_cheat_reset(void) { }
+RETRO_API void retro_cheat_set(unsigned index,
+ bool enabled, const char *code) { }
/* Loads a game. */
static system_type stype;
RETRO_API bool retro_load_game(const struct retro_game_info *game)
{
- if (game->path) {
- media.dir = path_dirname(game->path);
- media.name = basename_no_extension(game->path);
- media.extension = path_extension(game->path);
- }
- media.buffer = malloc(nearest_pow2(game->size));
- memcpy(media.buffer, game->data, game->size);
- media.size = game->size;
- stype = detect_system_type(&media);
- current_system = alloc_config_system(stype, &media, 0, 0);
-
unsigned format = RETRO_PIXEL_FORMAT_XRGB8888;
- retro_environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &format);
-
- return current_system != NULL;
+
+ if (game->path)
+ {
+ current_media.dir = path_dirname(game->path);
+ current_media.name = basename_no_extension(game->path);
+ current_media.extension = path_extension(game->path);
+ }
+
+ current_media.buffer = malloc(nearest_pow2(game->size));
+ memcpy(current_media.buffer, game->data, game->size);
+ current_media.size = game->size;
+ stype = detect_system_type(&current_media);
+ current_system = alloc_config_system(stype, &current_media, 0, 0);
+
+ retro_environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &format);
+
+ if (!current_system)
+ return false;
+ return true;
}
/* Loads a "special" kind of game. Should not be used,
* except in extreme cases. */
-RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
+RETRO_API bool retro_load_game_special(
+ unsigned game_type, const struct retro_game_info *info,
+ size_t num_info)
{
return retro_load_game(info);
}
@@ -256,14 +283,17 @@ RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_ga
/* Unloads a currently loaded game. */
RETRO_API void retro_unload_game(void)
{
- free(media.dir);
- free(media.name);
- free(media.extension);
- media.dir = media.name = media.extension = NULL;
- //buffer is freed by the context
- media.buffer = NULL;
+ free(current_media.dir);
+ free(current_media.name);
+ free(current_media.extension);
+ current_media.dir = NULL;
+ current_media.name = NULL;
+ current_media.extension = NULL;
+ /* buffer is freed by the context */
+ current_media.buffer = NULL;
+
current_system->free_context(current_system);
- current_system = NULL;
+ current_system = NULL;
}
/* Gets region of game. */
@@ -275,273 +305,238 @@ RETRO_API unsigned retro_get_region(void)
/* Gets region of memory. */
RETRO_API void *retro_get_memory_data(unsigned id)
{
- switch (id) {
- case RETRO_MEMORY_SYSTEM_RAM:
- switch (stype) {
- case SYSTEM_GENESIS: {
- genesis_context *gen = (genesis_context *)current_system;
- return (uint8_t *)gen->work_ram;
- }
+ switch (id)
+ {
+ case RETRO_MEMORY_SYSTEM_RAM:
+ switch (stype)
+ {
+ case SYSTEM_GENESIS:
+ {
+ genesis_context *gen = (genesis_context *)current_system;
+ return (uint8_t *)gen->work_ram;
+ }
#ifndef NO_Z80
- case SYSTEM_SMS: {
- sms_context *sms = (sms_context *)current_system;
- return sms->ram;
- }
+ case SYSTEM_SMS:
+ {
+ sms_context *sms = (sms_context *)current_system;
+ return sms->ram;
+ }
#endif
- }
- break;
- case RETRO_MEMORY_SAVE_RAM:
- if (stype == SYSTEM_GENESIS) {
- genesis_context *gen = (genesis_context *)current_system;
- if (gen->save_type != SAVE_NONE)
- return gen->save_storage;
- }
- break;
- default:
- break;
- }
+ }
+ break;
+ case RETRO_MEMORY_SAVE_RAM:
+ if (stype == SYSTEM_GENESIS)
+ {
+ genesis_context *gen = (genesis_context *)current_system;
+ if (gen->save_type != SAVE_NONE)
+ return gen->save_storage;
+ }
+ break;
+ default:
+ break;
+ }
return NULL;
}
RETRO_API size_t retro_get_memory_size(unsigned id)
{
- switch (id) {
- case RETRO_MEMORY_SYSTEM_RAM:
- switch (stype) {
- case SYSTEM_GENESIS:
- return RAM_WORDS * sizeof(uint16_t);
+ switch (id)
+ {
+ case RETRO_MEMORY_SYSTEM_RAM:
+ switch (stype)
+ {
+ case SYSTEM_GENESIS:
+ return RAM_WORDS * sizeof(uint16_t);
#ifndef NO_Z80
- case SYSTEM_SMS:
- return SMS_RAM_SIZE;
+ case SYSTEM_SMS:
+ return SMS_RAM_SIZE;
#endif
- }
- break;
- case RETRO_MEMORY_SAVE_RAM:
- if (stype == SYSTEM_GENESIS) {
- genesis_context *gen = (genesis_context *)current_system;
- if (gen->save_type != SAVE_NONE)
- return gen->save_size;
- }
- break;
- default:
- break;
- }
+ }
+ break;
+ case RETRO_MEMORY_SAVE_RAM:
+ if (stype == SYSTEM_GENESIS)
+ {
+ genesis_context *gen = (genesis_context *)current_system;
+ if (gen->save_type != SAVE_NONE)
+ return gen->save_size;
+ }
+ break;
+ default:
+ break;
+ }
return 0;
}
-//blastem render backend API implementation
+/* blastem render backend API implementation */
uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b)
{
return r << 16 | g << 8 | b;
}
-uint8_t render_create_window(char *caption, uint32_t width, uint32_t height, window_close_handler close_handler)
+/* Not supported in lib build */
+uint8_t render_create_window(char *caption,
+ uint32_t width, uint32_t height, window_close_handler close_handler)
{
- //not supported in lib build
return 0;
}
-void render_destroy_window(uint8_t which)
-{
- //not supported in lib build
-}
+/* Not supported in lib build */
+void render_destroy_window(uint8_t which) { }
static uint32_t fb[LINEBUF_SIZE * 294 * 2];
static uint8_t last_fb;
+
uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
{
- *pitch = LINEBUF_SIZE * sizeof(uint32_t);
- if (which != last_fb) {
- *pitch = *pitch * 2;
- }
+ *pitch = LINEBUF_SIZE * sizeof(uint32_t);
+ if (which != last_fb)
+ *pitch = *pitch * 2;
- if (which) {
- return fb + LINEBUF_SIZE;
- } else {
- return fb;
- }
+ if (which)
+ return fb + LINEBUF_SIZE;
+ return fb;
}
void render_framebuffer_updated(uint8_t which, int width)
{
- unsigned height = (video_standard == VID_NTSC ? 243 : 294) - (overscan_top + overscan_bot);
- width -= (overscan_left + overscan_right);
- unsigned base_height = height;
- if (which != last_fb) {
- height *= 2;
- last_fb = which;
- }
- if (width != last_width || height != last_height) {
- struct retro_game_geometry geometry = {
- .base_width = width,
- .base_height = height,
- .aspect_ratio = get_aspect_ratio()
- };
- retro_environment(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry);
- last_width = width;
- last_height = height;
- }
- retro_video_refresh(fb + overscan_left + LINEBUF_SIZE * overscan_top, width, height, LINEBUF_SIZE * sizeof(uint32_t));
- system_request_exit(current_system, 0);
-}
-
-uint8_t render_get_active_framebuffer(void)
-{
- return 0;
-}
-
-void render_set_video_standard(vid_std std)
-{
- video_standard = std;
-}
-
-int render_fullscreen(void)
-{
- return 1;
-}
-
-uint32_t render_overscan_top()
-{
- return overscan_top;
-}
-
-uint32_t render_overscan_bot()
-{
- return overscan_bot;
-}
-
-void process_events()
-{
+ unsigned base_height;
+ unsigned height = (video_standard == VID_NTSC ? 243 : 294)
+ - (overscan_top + overscan_bot);
+ width -= (overscan_left + overscan_right);
+ base_height = height;
+
+ if (which != last_fb)
+ {
+ height *= 2;
+ last_fb = which;
+ }
+ if (width != last_width || height != last_height)
+ {
+ struct retro_game_geometry geometry;
+ geometry.base_width = width;
+ geometry.base_height = height;
+ geometry.aspect_ratio = get_aspect_ratio();
+ retro_environment(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry);
+ last_width = width;
+ last_height = height;
+ }
+ retro_video_refresh(fb + overscan_left + LINEBUF_SIZE * overscan_top, width, height, LINEBUF_SIZE * sizeof(uint32_t));
+ system_request_exit(current_system, 0);
+}
+
+uint8_t render_get_active_framebuffer(void) { return 0; }
+void render_set_video_standard(vid_std std) { video_standard = std; }
+int render_fullscreen(void) { return 1; }
+uint32_t render_overscan_top(void) { return overscan_top; }
+uint32_t render_overscan_bot(void) { return overscan_bot; }
+
+void process_events(void)
+{
+ int port;
+ int16_t inputs[2];
static int16_t prev_state[2][RETRO_DEVICE_ID_JOYPAD_L2];
static const uint8_t map[] = {
BUTTON_B, BUTTON_A, BUTTON_MODE, BUTTON_START, DPAD_UP, DPAD_DOWN,
DPAD_LEFT, DPAD_RIGHT, BUTTON_C, BUTTON_Y, BUTTON_X, BUTTON_Z
};
- //TODO: handle other input device types
- //TODO: handle more than 2 ports when appropriate
- retro_input_poll();
- for (int port = 0; port < 2; port++)
- {
- for (int id = RETRO_DEVICE_ID_JOYPAD_B; id < RETRO_DEVICE_ID_JOYPAD_L2; id++)
- {
- int16_t new_state = retro_input_state(port, RETRO_DEVICE_JOYPAD, 0, id);
- if (new_state != prev_state[port][id]) {
- if (new_state) {
- current_system->gamepad_down(current_system, port + 1, map[id]);
- } else {
- current_system->gamepad_up(current_system, port + 1, map[id]);
- }
- prev_state[port][id] = new_state;
- }
- }
- }
-}
-
-void render_errorbox(char *title, char *message)
-{
-}
-void render_warnbox(char *title, char *message)
-{
-}
-void render_infobox(char *title, char *message)
-{
-}
-
-uint8_t render_is_audio_sync(void)
-{
- //whether this is true depends on the libretro frontend implementation
- //but the sync to audio path works better here
- return 1;
-}
-
-uint8_t render_should_release_on_exit(void)
-{
- return 0;
-}
-
-void render_buffer_consumed(audio_source *src)
-{
-}
-
-void *render_new_audio_opaque(void)
-{
- return NULL;
-}
-
-void render_free_audio_opaque(void *opaque)
-{
-}
-
-void render_lock_audio(void)
-{
-}
-void render_unlock_audio()
-{
-}
-
-uint32_t render_min_buffered(void)
-{
- //not actually used in the sync to audio path
- return 4;
-}
-
-uint32_t render_audio_syncs_per_sec(void)
-{
- return 0;
-}
+ /* TODO: handle other input device types
+ * TODO: handle more than 2 ports when appropriate
+ */
+ retro_input_poll();
-void render_audio_created(audio_source *src)
-{
-}
+ inputs[0] = 0;
+ inputs[1] = 0;
+
+ if (libretro_supports_bitmasks)
+ {
+ inputs[0] = retro_input_state(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
+ inputs[1] = retro_input_state(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
+ }
+ else
+ {
+ for (port = 0; port < 2; port++)
+ {
+ int id;
+ for (id = RETRO_DEVICE_ID_JOYPAD_B; id < RETRO_DEVICE_ID_JOYPAD_L2; id++)
+ {
+ if (retro_input_state(port, RETRO_DEVICE_JOYPAD, 0, id))
+ inputs[port] |= (1 << id);
+ }
+ }
+ }
+
+ for (port = 0; port < 2; port++)
+ {
+ int id;
+ for (id = RETRO_DEVICE_ID_JOYPAD_B; id < RETRO_DEVICE_ID_JOYPAD_L2; id++)
+ {
+ int16_t new_state = inputs[port] & (1 << id);
+ if (new_state != prev_state[port][id])
+ {
+ if (new_state)
+ current_system->gamepad_down(current_system, port + 1, map[id]);
+ else
+ current_system->gamepad_up(current_system, port + 1, map[id]);
+ prev_state[port][id] = new_state;
+ }
+ }
+ }
+}
+
+void render_errorbox(char *title, char *message) { }
+void render_warnbox(char *title, char *message) { }
+void render_infobox(char *title, char *message) { }
+
+/* Whether this is true depends on the
+ * libretro frontend implementation,
+ * but the sync to audio path works better here */
+uint8_t render_is_audio_sync(void) { return 1; }
+uint8_t render_should_release_on_exit(void) { return 0; }
+void render_buffer_consumed(audio_source *src) { }
+void *render_new_audio_opaque(void) { return NULL; }
+
+void render_free_audio_opaque(void *opaque) { }
+void render_lock_audio(void) { }
+void render_unlock_audio(void) { }
+/*not actually used in the sync to audio path */
+uint32_t render_min_buffered(void) { return 4; }
+uint32_t render_audio_syncs_per_sec(void) { return 0; }
+void render_audio_created(audio_source *src) { }
void render_do_audio_ready(audio_source *src)
{
- int16_t *tmp = src->front;
- src->front = src->back;
- src->back = tmp;
- src->front_populated = 1;
- src->buffer_pos = 0;
- if (all_sources_ready()) {
- int16_t buffer[8];
- int min_remaining_out;
- mix_and_convert((uint8_t *)buffer, sizeof(buffer), &min_remaining_out);
- retro_audio_sample_batch(buffer, sizeof(buffer)/(2*sizeof(*buffer)));
- }
-}
-
-void render_source_paused(audio_source *src, uint8_t remaining_sources)
-{
-}
-
-void render_source_resumed(audio_source *src)
-{
-}
+ int16_t *tmp = src->front;
+ src->front = src->back;
+ src->back = tmp;
+ src->front_populated = 1;
+ src->buffer_pos = 0;
-void render_set_external_sync(uint8_t ext_sync_on)
-{
+ if (all_sources_ready())
+ {
+ int16_t buffer[8];
+ int min_remaining_out;
+ mix_and_convert((uint8_t *)buffer, sizeof(buffer), &min_remaining_out);
+ retro_audio_sample_batch(buffer, sizeof(buffer)/(2*sizeof(*buffer)));
+ }
}
-void bindings_set_mouse_mode(uint8_t mode)
-{
-}
-
-void bindings_release_capture(void)
-{
-}
-
-void bindings_reacquire_capture(void)
-{
-}
+void render_source_paused(audio_source *src, uint8_t remaining_sources) { }
+void render_source_resumed(audio_source *src) { }
+void render_set_external_sync(uint8_t ext_sync_on) { }
+void bindings_set_mouse_mode(uint8_t mode) { }
+void bindings_release_capture(void) { }
+void bindings_reacquire_capture(void) { }
extern const char rom_db_data[];
char *read_bundled_file(char *name, uint32_t *sizeret)
{
- if (!strcmp(name, "rom.db")) {
- *sizeret = strlen(rom_db_data);
- char *ret = malloc(*sizeret+1);
- memcpy(ret, rom_db_data, *sizeret + 1);
- return ret;
- }
+ if (!strcmp(name, "rom.db"))
+ {
+ *sizeret = strlen(rom_db_data);
+ char *ret = malloc(*sizeret+1);
+ memcpy(ret, rom_db_data, *sizeret + 1);
+ return ret;
+ }
return NULL;
}