#include "init.h"
#include "tools.h"
#include "tape.h"
+#include "config.h"
#define ENABLE_UNUSED_CODE 0 /* currently unused functions */
#define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
{ LEVEL_FILE_TYPE_DX, "DX" },
{ LEVEL_FILE_TYPE_SB, "SB" },
{ LEVEL_FILE_TYPE_DC, "DC" },
+ { LEVEL_FILE_TYPE_MM, "MM" },
+ { LEVEL_FILE_TYPE_MM, "DF" },
{ -1, NULL },
};
level->native_em_level = &native_em_level;
level->native_sp_level = &native_sp_level;
+ level->native_mm_level = &native_mm_level;
level->file_version = FILE_VERSION_ACTUAL;
level->game_version = GAME_VERSION_ACTUAL;
if (fileExists(lfi->filename))
return;
}
+ else if (leveldir_current->level_filetype != NULL)
+ {
+ int filetype = getFiletypeFromID(leveldir_current->level_filetype);
+
+ /* check for specified native level file with standard file name */
+ setLevelFileInfo_FormatLevelFilename(lfi, filetype,
+ "%03d.%s", nr, LEVELFILE_EXTENSION);
+ if (fileExists(lfi->filename))
+ return;
+ }
/* check for native Rocks'n'Diamonds level file */
setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+ if (!setup.editor.use_template_for_new_levels)
+ return;
+
/* if level file not found, try to initialize level data from template */
filename = getGlobalLevelTemplateFilename();
{
struct LevelInfo_SP *level_sp = level->native_sp_level;
LevelInfoType *header = &level_sp->header;
- int i, x, y;
+ boolean num_invalid_elements = 0;
+ int i, j, x, y;
level->fieldx = level_sp->width;
level->fieldy = level_sp->height;
int element_new = getMappedElement(map_element_SP_to_RND(element_old));
if (element_new == EL_UNKNOWN)
- Error(ERR_WARN, "invalid element %d at position %d, %d",
+ {
+ num_invalid_elements++;
+
+ Error(ERR_DEBUG, "invalid element %d at position %d, %d",
element_old, x, y);
+ }
level->field[x][y] = element_new;
}
}
+ if (num_invalid_elements > 0)
+ Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
+ (!options.debug ? " (use '--debug' for more details)" : ""));
+
for (i = 0; i < MAX_PLAYERS; i++)
level->initial_player_gravity[i] =
(header->InitialGravity == 1 ? TRUE : FALSE);
+ /* skip leading spaces */
for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
- level->name[i] = header->LevelTitle[i];
- level->name[SP_LEVEL_NAME_LEN] = '\0';
+ if (header->LevelTitle[i] != ' ')
+ break;
+
+ /* copy level title */
+ for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
+ level->name[j] = header->LevelTitle[i];
+ level->name[j] = '\0';
+
+ /* cut trailing spaces */
+ for (; j > 0; j--)
+ if (level->name[j - 1] == ' ' && level->name[j] == '\0')
+ level->name[j - 1] = '\0';
level->gems_needed = header->InfotronsNeeded;
level_sp->header.DemoRandomSeed = tape.random_seed;
demo->length = 0;
+
for (i = 0; i < tape.length; i++)
{
int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
int demo_repeat = tape.pos[i].delay;
+ int demo_entries = (demo_repeat + 15) / 16;
+
+ if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
+ {
+ Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
+ SP_MAX_TAPE_LEN);
+
+ break;
+ }
for (j = 0; j < demo_repeat / 16; j++)
demo->data[demo->length++] = 0xf0 | demo_action;
demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
}
- demo->data[demo->length++] = 0xff;
-
demo->is_available = TRUE;
}
return;
tape.level_nr = demo->level_nr; /* (currently not used) */
- tape.length = demo->length - 1; /* without "end of demo" byte */
tape.random_seed = level_sp->header.DemoRandomSeed;
TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
- for (i = 0; i < demo->length - 1; i++)
+ tape.counter = 0;
+ tape.pos[tape.counter].delay = 0;
+
+ for (i = 0; i < demo->length; i++)
{
int demo_action = demo->data[i] & 0x0f;
int demo_repeat = (demo->data[i] & 0xf0) >> 4;
+ int tape_action = map_key_SP_to_RND(demo_action);
+ int tape_repeat = demo_repeat + 1;
+ byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
+ boolean success = 0;
+ int j;
+
+ for (j = 0; j < tape_repeat; j++)
+ success = TapeAddAction(action);
- tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
- tape.pos[i].delay = demo_repeat + 1;
+ if (!success)
+ {
+ Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
+ MAX_TAPE_LEN);
+
+ break;
+ }
}
- tape.length_frames = GetTapeLengthFrames();
- tape.length_seconds = GetTapeLengthSeconds();
+ TapeHaltRecording();
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading MM level */
+/* ------------------------------------------------------------------------- */
+
+void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
+{
+ struct LevelInfo_MM *level_mm = level->native_mm_level;
+ int x, y;
+
+ level_mm->file_version = level->file_version;
+ level_mm->game_version = level->game_version;
+ level_mm->encoding_16bit_field = level->encoding_16bit_field;
+
+ level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
+ level_mm->fieldy = MIN(level->fieldx, MM_MAX_PLAYFIELD_HEIGHT);
+
+ level_mm->time = level->time;
+ level_mm->kettles_needed = level->gems_needed;
+ level_mm->auto_count_kettles = FALSE;
+ level_mm->laser_red = FALSE;
+ level_mm->laser_green = FALSE;
+ level_mm->laser_blue = TRUE;
+
+ strcpy(level_mm->name, level->name);
+ strcpy(level_mm->author, level->author);
+
+ level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
+ level_mm->score[SC_KEY] = level->score[SC_PACMAN];
+ level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
+
+ level_mm->amoeba_speed = level->amoeba_speed;
+ level_mm->time_fuse = 0;
+
+ for (y = 0; y < level_mm->fieldx; y++)
+ for (x = 0; x < level_mm->fieldy; x++)
+ level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
+}
+
+void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
+{
+ struct LevelInfo_MM *level_mm = level->native_mm_level;
+ int x, y;
+
+ level->file_version = level_mm->file_version;
+ level->game_version = level_mm->game_version;
+ level->encoding_16bit_field = level_mm->encoding_16bit_field;
+
+ level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
+ level->fieldy = MIN(level_mm->fieldx, MAX_LEV_FIELDY);
+
+ level->time = level_mm->time;
+ level->gems_needed = level_mm->kettles_needed;
+
+ strcpy(level->name, level_mm->name);
+ strcpy(level->author, level_mm->author);
+
+ level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
+ level->score[SC_KEY] = level_mm->score[SC_PACMAN];
+ level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
+
+ level->amoeba_speed = level_mm->amoeba_speed;
+
+ for (y = 0; y < level->fieldx; y++)
+ for (x = 0; x < level->fieldy; x++)
+ level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
}
level->no_valid_file = TRUE;
}
+static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info,
+ boolean level_info_only)
+{
+ if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
+ level->no_valid_file = TRUE;
+}
+
void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
{
if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
CopyNativeLevel_RND_to_EM(level);
else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
CopyNativeLevel_RND_to_SP(level);
+ else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
+ CopyNativeLevel_RND_to_MM(level);
}
void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
CopyNativeLevel_EM_to_RND(level);
else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
CopyNativeLevel_SP_to_RND(level);
+ else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
+ CopyNativeLevel_MM_to_RND(level);
}
void SaveNativeLevel(struct LevelInfo *level)
level->game_engine_type = GAME_ENGINE_TYPE_SP;
break;
+ case LEVEL_FILE_TYPE_MM:
+ LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
+ level->game_engine_type = GAME_ENGINE_TYPE_MM;
+ break;
+
case LEVEL_FILE_TYPE_DC:
LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
break;
for (i = 0; i < tape->length; i++)
{
if (i >= MAX_TAPE_LEN)
+ {
+ Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
+ MAX_TAPE_LEN);
+
+ // tape too large; read and ignore remaining tape data from this chunk
+ for (;i < tape->length; i++)
+ ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
+
break;
+ }
for (j = 0; j < MAX_PLAYERS; j++)
{
#define SETUP_TOKEN_TEAM_MODE 14
#define SETUP_TOKEN_HANDICAP 15
#define SETUP_TOKEN_SKIP_LEVELS 16
-#define SETUP_TOKEN_TIME_LIMIT 17
-#define SETUP_TOKEN_FULLSCREEN 18
-#define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19
-#define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20
-#define SETUP_TOKEN_SCREEN_RENDERING_MODE 21
-#define SETUP_TOKEN_ASK_ON_ESCAPE 22
-#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 23
-#define SETUP_TOKEN_QUICK_SWITCH 24
-#define SETUP_TOKEN_INPUT_ON_FOCUS 25
-#define SETUP_TOKEN_PREFER_AGA_GRAPHICS 26
-#define SETUP_TOKEN_GAME_FRAME_DELAY 27
-#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 28
-#define SETUP_TOKEN_SMALL_GAME_GRAPHICS 29
-#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 30
-#define SETUP_TOKEN_GRAPHICS_SET 31
-#define SETUP_TOKEN_SOUNDS_SET 32
-#define SETUP_TOKEN_MUSIC_SET 33
-#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 34
-#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 35
-#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 36
-#define SETUP_TOKEN_VOLUME_SIMPLE 37
-#define SETUP_TOKEN_VOLUME_LOOPS 38
-#define SETUP_TOKEN_VOLUME_MUSIC 39
-#define SETUP_TOKEN_TOUCH_CONTROL_TYPE 40
-#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 41
-#define SETUP_TOKEN_TOUCH_DROP_DISTANCE 42
-
-#define NUM_GLOBAL_SETUP_TOKENS 43
+#define SETUP_TOKEN_INCREMENT_LEVELS 17
+#define SETUP_TOKEN_TIME_LIMIT 18
+#define SETUP_TOKEN_FULLSCREEN 19
+#define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
+#define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
+#define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
+#define SETUP_TOKEN_ASK_ON_ESCAPE 23
+#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
+#define SETUP_TOKEN_QUICK_SWITCH 25
+#define SETUP_TOKEN_INPUT_ON_FOCUS 26
+#define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
+#define SETUP_TOKEN_GAME_FRAME_DELAY 28
+#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
+#define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
+#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
+#define SETUP_TOKEN_GRAPHICS_SET 32
+#define SETUP_TOKEN_SOUNDS_SET 33
+#define SETUP_TOKEN_MUSIC_SET 34
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
+#define SETUP_TOKEN_VOLUME_SIMPLE 38
+#define SETUP_TOKEN_VOLUME_LOOPS 39
+#define SETUP_TOKEN_VOLUME_MUSIC 40
+#define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
+#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
+#define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
+
+#define NUM_GLOBAL_SETUP_TOKENS 44
/* editor setup */
#define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
/* internal setup */
#define SETUP_TOKEN_INT_PROGRAM_TITLE 0
-#define SETUP_TOKEN_INT_PROGRAM_AUTHOR 1
-#define SETUP_TOKEN_INT_PROGRAM_EMAIL 2
-#define SETUP_TOKEN_INT_PROGRAM_WEBSITE 3
-#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 4
-#define SETUP_TOKEN_INT_PROGRAM_COMPANY 5
-#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 6
-#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 7
-#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 8
-#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 9
-#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 10
-#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 11
-#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 12
-#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 13
-#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
-#define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 15
-#define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 16
-
-#define NUM_INTERNAL_SETUP_TOKENS 17
+#define SETUP_TOKEN_INT_PROGRAM_VERSION 1
+#define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
+#define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
+#define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
+#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
+#define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
+#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
+#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
+#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
+#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
+#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
+#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
+#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
+#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
+#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
+#define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
+#define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
+#define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
+
+#define NUM_INTERNAL_SETUP_TOKENS 19
/* debug setup */
#define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
#define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
#define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
#define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
+#define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
-#define NUM_DEBUG_SETUP_TOKENS 22
+#define NUM_DEBUG_SETUP_TOKENS 23
/* options setup */
#define SETUP_TOKEN_OPTIONS_VERBOSE 0
{ TYPE_SWITCH, &si.team_mode, "team_mode" },
{ TYPE_SWITCH, &si.handicap, "handicap" },
{ TYPE_SWITCH, &si.skip_levels, "skip_levels" },
+ { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
{ TYPE_SWITCH, &si.time_limit, "time_limit" },
{ TYPE_SWITCH, &si.fullscreen, "fullscreen" },
{ TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
{ TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
{ TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
{ TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
+ { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
+ { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
{ TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
{ TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
{ TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
static struct TokenInfo internal_setup_tokens[] =
{
{ TYPE_STRING, &sxi.program_title, "program_title" },
+ { TYPE_STRING, &sxi.program_version, "program_version" },
{ TYPE_STRING, &sxi.program_author, "program_author" },
{ TYPE_STRING, &sxi.program_email, "program_email" },
{ TYPE_STRING, &sxi.program_website, "program_website" },
{ TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
{ TYPE_STRING, &sxi.default_level_series, "default_level_series" },
{ TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
+ { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
{ TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
{ TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
};
{ TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
{ TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
{ TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
+ { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
};
static struct TokenInfo options_setup_tokens[] =
si->team_mode = FALSE;
si->handicap = TRUE;
si->skip_levels = TRUE;
+ si->increment_levels = TRUE;
si->time_limit = TRUE;
si->fullscreen = FALSE;
si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
si->editor.el_supaplex = TRUE;
si->editor.el_diamond_caves = TRUE;
si->editor.el_dx_boulderdash = TRUE;
+
+ si->editor.el_mirror_magic = TRUE;
+ si->editor.el_deflektor = TRUE;
+
si->editor.el_chars = TRUE;
si->editor.el_steel_chars = TRUE;
si->editor.show_element_token = FALSE;
+ si->editor.use_template_for_new_levels = TRUE;
+
si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
+ si->internal.program_version = getStringCopy(getProgramRealVersionString());
si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
si->internal.choose_from_top_leveldir = FALSE;
+ si->internal.show_scaling_in_title = TRUE;
si->internal.default_window_width = WIN_XSIZE_DEFAULT;
si->internal.default_window_height = WIN_YSIZE_DEFAULT;
si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
+ si->debug.show_frames_per_second = FALSE;
+
si->options.verbose = FALSE;
#if defined(PLATFORM_ANDROID)
si->editor_cascade.el_dc = TRUE;
si->editor_cascade.el_dx = TRUE;
+ si->editor_cascade.el_mm = TRUE;
+ si->editor_cascade.el_df = TRUE;
+
si->editor_cascade.el_chars = FALSE;
si->editor_cascade.el_steel_chars = FALSE;
si->editor_cascade.el_ce = FALSE;
si->editor_cascade.el_dynamic = FALSE;
}
+#define MAX_HIDE_SETUP_TOKEN_SIZE 20
+
+static char *getHideSetupToken(void *setup_value)
+{
+ static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
+
+ if (setup_value != NULL)
+ snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
+
+ return hide_setup_token;
+}
+
+static void setHideSetupEntry(void *setup_value_raw)
+{
+ /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
+ void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
+
+ char *hide_setup_token = getHideSetupToken(setup_value);
+
+ if (setup_value != NULL)
+ setHashEntry(hide_setup_hash, hide_setup_token, "");
+}
+
+boolean hideSetupEntry(void *setup_value)
+{
+ char *hide_setup_token = getHideSetupToken(setup_value);
+
+ return (setup_value != NULL &&
+ getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
+}
+
+static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
+ struct TokenInfo *token_info,
+ int token_nr, char *token_text)
+{
+ char *token_hide_text = getStringCat2(token_text, ".hide");
+ char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
+
+ /* set the value of this setup option in the setup option structure */
+ setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
+
+ /* check if this setup option should be hidden in the setup menu */
+ if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
+ setHideSetupEntry(token_info[token_nr].value);
+}
+
+static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
+ struct TokenInfo *token_info,
+ int token_nr)
+{
+ setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
+ token_info[token_nr].text);
+}
+
static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
{
int i, pnr;
if (!setup_file_hash)
return;
+ if (hide_setup_hash == NULL)
+ hide_setup_hash = newSetupFileHash();
+
/* global setup */
si = setup;
for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
- setSetupInfo(global_setup_tokens, i,
- getHashEntry(setup_file_hash, global_setup_tokens[i].text));
+ setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
setup = si;
/* editor setup */
sei = setup.editor;
for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
- setSetupInfo(editor_setup_tokens, i,
- getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
+ setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
setup.editor = sei;
/* shortcut setup */
ssi = setup.shortcut;
for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
- setSetupInfo(shortcut_setup_tokens, i,
- getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
+ setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
setup.shortcut = ssi;
/* player setup */
char full_token[100];
sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
- setSetupInfo(player_setup_tokens, i,
- getHashEntry(setup_file_hash, full_token));
+ setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
+ full_token);
}
setup.input[pnr] = sii;
}
/* system setup */
syi = setup.system;
for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
- setSetupInfo(system_setup_tokens, i,
- getHashEntry(setup_file_hash, system_setup_tokens[i].text));
+ setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
setup.system = syi;
/* internal setup */
sxi = setup.internal;
for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
- setSetupInfo(internal_setup_tokens, i,
- getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
+ setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
setup.internal = sxi;
/* debug setup */
sdi = setup.debug;
for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
- setSetupInfo(debug_setup_tokens, i,
- getHashEntry(setup_file_hash, debug_setup_tokens[i].text));
+ setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
setup.debug = sdi;
/* options setup */
soi = setup.options;
for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
- setSetupInfo(options_setup_tokens, i,
- getHashEntry(setup_file_hash, options_setup_tokens[i].text));
+ setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
setup.options = soi;
}
}
else
{
- Error(ERR_WARN, "using default setup values");
+ Error(ERR_DEBUG, "using default setup values");
}
}
free(filename);
}
+static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
+ char *mapping_line)
+{
+ char mapping_guid[MAX_LINE_LEN];
+ char *mapping_start, *mapping_end;
+
+ // get GUID from game controller mapping line: copy complete line
+ strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
+ mapping_guid[MAX_LINE_LEN - 1] = '\0';
+
+ // get GUID from game controller mapping line: cut after GUID part
+ mapping_start = strchr(mapping_guid, ',');
+ if (mapping_start != NULL)
+ *mapping_start = '\0';
+
+ // cut newline from game controller mapping line
+ mapping_end = strchr(mapping_line, '\n');
+ if (mapping_end != NULL)
+ *mapping_end = '\0';
+
+ // add mapping entry to game controller mappings hash
+ setHashEntry(mappings_hash, mapping_guid, mapping_line);
+}
+
+static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
+ char *filename)
+{
+ FILE *file;
+
+ if (!(file = fopen(filename, MODE_READ)))
+ {
+ Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
+
+ return;
+ }
+
+ while (!feof(file))
+ {
+ char line[MAX_LINE_LEN];
+
+ if (!fgets(line, MAX_LINE_LEN, file))
+ break;
+
+ addGameControllerMappingToHash(mappings_hash, line);
+ }
+
+ fclose(file);
+}
+
void SaveSetup()
{
char *filename = getSetupFilename();
free(filename);
}
+static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
+ char *filename)
+{
+ FILE *file;
+
+ if (!(file = fopen(filename, MODE_WRITE)))
+ {
+ Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
+
+ return;
+ }
+
+ BEGIN_HASH_ITERATION(mappings_hash, itr)
+ {
+ fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
+ }
+ END_HASH_ITERATION(mappings_hash, itr)
+
+ fclose(file);
+}
+
+void SaveSetup_AddGameControllerMapping(char *mapping)
+{
+ char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
+ SetupFileHash *mappings_hash = newSetupFileHash();
+
+ InitUserDataDirectory();
+
+ // load existing personal game controller mappings
+ LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
+
+ // add new mapping to personal game controller mappings
+ addGameControllerMappingToHash(mappings_hash, mapping);
+
+ // save updated personal game controller mappings
+ SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
+
+ freeSetupFileHash(mappings_hash);
+ free(filename);
+}
+
void LoadCustomElementDescriptions()
{
char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);