// (c) 1995-2014 by Artsoft Entertainment
// Holger Schemel
// info@artsoft.org
-// http://www.artsoft.org/
+// https://www.artsoft.org/
// ----------------------------------------------------------------------------
// files.c
// ============================================================================
#include "tools.h"
#include "tape.h"
#include "config.h"
+#include "api.h"
#define ENABLE_UNUSED_CODE 0 // currently unused functions
#define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
// (element number only)
#define LEVEL_CHUNK_GRPX_UNCHANGED 2
+#define LEVEL_CHUNK_EMPX_UNCHANGED 2
#define LEVEL_CHUNK_NOTE_UNCHANGED 2
// (nothing at all if unchanged)
#define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
#define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
-#define TAPE_CHUNK_HEAD_UNUSED 2 // unused tape header bytes
+#define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
+
+#define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
#define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
#define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
// file identifier strings
#define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
#define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
-#define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
+#define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
// values for deciding when (not) to save configuration data
#define SAVE_CONF_NEVER 0
CONF_CONTENT_NUM_BYTES : 1)
#define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
-#define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
+#define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
(b[CONF_ELEMENT_BYTE_POS(i) + 1]))
#define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
&li.solved_by_one_player, FALSE
},
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(12),
+ &li.time_score_base, 1
+ },
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
+ &li.rate_time_over_score, FALSE
+ },
+
{
-1, -1,
-1, -1,
TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
&li.lazy_relocation, FALSE
},
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
+ &li.finish_dig_collect, TRUE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
+ &li.keep_walkable_ce, FALSE
+ },
// (these values are different for each player)
{
&li.android_clone_time, 10
},
{
- EL_EMC_ANDROID, -1,
+ EL_EMC_ANDROID, SAVE_CONF_NEVER,
TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
&li.android_clone_element[0], EL_EMPTY, NULL,
+ &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
+ },
+ {
+ EL_EMC_ANDROID, -1,
+ TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
+ &li.android_clone_element[0], EL_EMPTY, NULL,
&li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
},
{
EL_EMC_MAGIC_BALL, -1,
TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
- &li.ball_state_initial, FALSE
+ &li.ball_active_initial, FALSE
},
{
EL_EMC_MAGIC_BALL, -1,
TYPE_INTEGER, CONF_VALUE_16_BIT(1),
&li.mm_time_bomb, 75
},
+
{
EL_MM_GRAY_BALL, -1,
TYPE_INTEGER, CONF_VALUE_16_BIT(1),
&li.mm_time_ball, 75
},
+ {
+ EL_MM_GRAY_BALL, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(1),
+ &li.mm_ball_choice_mode, ANIM_RANDOM
+ },
+ {
+ EL_MM_GRAY_BALL, -1,
+ TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
+ &li.mm_ball_content, EL_EMPTY, NULL,
+ &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
+ },
+ {
+ EL_MM_GRAY_BALL, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
+ &li.rotate_mm_ball_content, TRUE
+ },
+ {
+ EL_MM_GRAY_BALL, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
+ &li.explode_mm_ball, FALSE
+ },
+
{
EL_MM_STEEL_BLOCK, -1,
TYPE_INTEGER, CONF_VALUE_16_BIT(1),
&xx_ei.move_delay_random, 0,
&yy_ei.move_delay_random
},
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(16),
+ &xx_ei.step_delay_fixed, 0,
+ &yy_ei.step_delay_fixed
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(17),
+ &xx_ei.step_delay_random, 0,
+ &yy_ei.step_delay_random
+ },
{
-1, -1,
}
};
+static struct LevelFileConfigInfo chunk_config_EMPX[] =
+{
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &xx_ei.use_gfx_element, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1
+ }
+};
+
static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
{
{
int element = i;
struct ElementInfo *ei = &element_info[element];
+ if (element == EL_MM_GRAY_BALL)
+ {
+ struct LevelInfo_MM *level_mm = level->native_mm_level;
+ int j;
+
+ for (j = 0; j < level->num_mm_ball_contents; j++)
+ level->mm_ball_content[j] =
+ map_element_MM_to_RND(level_mm->ball_content[j]);
+ }
+
// never initialize clipboard elements after the very first time
// (to be able to use clipboard elements between several levels)
if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
*group = xx_group;
}
+
+ if (IS_EMPTY_ELEMENT(element) ||
+ IS_INTERNAL_ELEMENT(element))
+ {
+ xx_ei = *ei; // copy element data into temporary buffer
+
+ setConfigToDefaultsFromConfigList(chunk_config_EMPX);
+
+ *ei = xx_ei;
+ }
}
clipboard_elements_initialized = TRUE;
if ((dir = openDirectory(directory)) == NULL)
{
- Error(ERR_WARN, "cannot read current level directory '%s'", directory);
+ Warn("cannot read current level directory '%s'", directory);
return basename;
}
// functions for loading R'n'D level
// ----------------------------------------------------------------------------
-static int getMappedElement(int element)
+int getMappedElement(int element)
{
// remap some (historic, now obsolete) elements
default:
if (element >= NUM_FILE_ELEMENTS)
{
- Error(ERR_WARN, "invalid level element %d", element);
+ Warn("invalid level element %d", element);
element = EL_UNKNOWN;
}
}
else
{
- Error(ERR_WARN, "cannot load content for element '%d'", element);
+ Warn("cannot load content for element '%d'", element);
}
return chunk_size;
if (IS_CUSTOM_ELEMENT(element))
element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
else
- Error(ERR_WARN, "invalid custom element number %d", element);
+ Warn("invalid custom element number %d", element);
// older game versions that wrote level files with CUS1 chunks used
// different default push delay values (not yet stored in level file)
if (IS_CUSTOM_ELEMENT(element))
element_info[element].change->target_element = custom_target_element;
else
- Error(ERR_WARN, "invalid custom element number %d", element);
+ Warn("invalid custom element number %d", element);
}
level->file_has_custom_elements = TRUE;
if (!IS_CUSTOM_ELEMENT(element))
{
- Error(ERR_WARN, "invalid custom element number %d", element);
+ Warn("invalid custom element number %d", element);
element = EL_INTERNAL_DUMMY;
}
for (x = 0; x < 3; x++)
ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
+ // bits 0 - 31 of "has_event[]"
event_bits = getFile32BitBE(file);
- for (j = 0; j < NUM_CHANGE_EVENTS; j++)
- if (event_bits & (1 << j))
+ for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
+ if (event_bits & (1u << j))
ei->change->has_event[j] = TRUE;
ei->change->target_element = getMappedElement(getFile16BitBE(file));
if (!IS_CUSTOM_ELEMENT(element))
{
- Error(ERR_WARN, "invalid custom element number %d", element);
+ Warn("invalid custom element number %d", element);
ReadUnusedBytesFromFile(file, chunk_size - 2);
+
return chunk_size;
}
// bits 0 - 31 of "has_event[]" ...
event_bits = getFile32BitBE(file);
for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
- if (event_bits & (1 << j))
+ if (event_bits & (1u << j))
change->has_event[j] = TRUE;
change->target_element = getMappedElement(getFile16BitBE(file));
// ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
event_bits = getFile8Bit(file);
for (j = 32; j < NUM_CHANGE_EVENTS; j++)
- if (event_bits & (1 << (j - 32)))
+ if (event_bits & (1u << (j - 32)))
change->has_event[j] = TRUE;
}
if (!IS_GROUP_ELEMENT(element))
{
- Error(ERR_WARN, "invalid group element number %d", element);
+ Warn("invalid group element number %d", element);
ReadUnusedBytesFromFile(file, chunk_size - 2);
+
return chunk_size;
}
if (num_entities > max_num_entities)
{
- Error(ERR_WARN,
- "truncating number of entities for element %d from %d to %d",
- element, num_entities, max_num_entities);
+ Warn("truncating number of entities for element %d from %d to %d",
+ element, num_entities, max_num_entities);
num_entities = max_num_entities;
}
data_type == TYPE_CONTENT_LIST))
{
// for element and content lists, zero entities are not allowed
- Error(ERR_WARN, "found empty list of entities for element %d",
- element);
+ Warn("found empty list of entities for element %d", element);
// do not set "num_entities" here to prevent reading behind buffer
value = getMappedElement(value);
if (data_type == TYPE_BOOLEAN)
- *(boolean *)(conf[i].value) = value;
+ *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
else
*(int *) (conf[i].value) = value;
int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
int error_element = real_element;
- Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
- error_conf_chunk_bytes, error_conf_chunk_token,
- error_element, EL_NAME(error_element));
+ Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
+ error_conf_chunk_bytes, error_conf_chunk_token,
+ error_element, EL_NAME(error_element));
}
return micro_chunk_size;
if (ei->num_change_pages == -1)
{
- Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
- EL_NAME(element));
+ Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
+ EL_NAME(element));
ei->num_change_pages = 1;
while (!checkEndOfFile(file))
{
+ // level file might contain invalid change page number
+ if (xx_current_change_page >= ei->num_change_pages)
+ break;
+
struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
xx_change = *change; // copy change data into temporary buffer
struct ElementInfo *ei = &element_info[element];
struct ElementGroupInfo *group = ei->group;
+ if (group == NULL)
+ return -1;
+
xx_ei = *ei; // copy element data into temporary buffer
xx_group = *group; // copy group data into temporary buffer
return real_chunk_size;
}
+static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
+{
+ int element = getMappedElement(getFile16BitBE(file));
+ int real_chunk_size = 2;
+ struct ElementInfo *ei = &element_info[element];
+
+ xx_ei = *ei; // copy element data into temporary buffer
+
+ while (!checkEndOfFile(file))
+ {
+ real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
+ -1, element);
+
+ if (real_chunk_size >= chunk_size)
+ break;
+ }
+
+ *ei = xx_ei;
+
+ level->file_has_custom_elements = TRUE;
+
+ return real_chunk_size;
+}
+
static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
struct LevelFileInfo *level_file_info,
boolean level_info_only)
if (level_info_only)
return;
- Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+ Warn("cannot read level '%s' -- using empty level", filename);
if (!setup.editor.use_template_for_new_levels)
return;
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "unknown format of level file '%s'", filename);
+ Warn("unknown format of level file '%s'", filename);
closeFile(file);
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "unknown format of level file '%s'", filename);
+ Warn("unknown format of level file '%s'", filename);
closeFile(file);
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "unsupported version of level file '%s'", filename);
+ Warn("unsupported version of level file '%s'", filename);
closeFile(file);
{ "NOTE", -1, LoadLevel_NOTE },
{ "CUSX", -1, LoadLevel_CUSX },
{ "GRPX", -1, LoadLevel_GRPX },
+ { "EMPX", -1, LoadLevel_EMPX },
{ NULL, 0, NULL }
};
if (chunk_info[i].name == NULL)
{
- Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
- chunk_name, filename);
+ Warn("unknown chunk '%s' in level file '%s'",
+ chunk_name, filename);
+
ReadUnusedBytesFromFile(file, chunk_size);
}
else if (chunk_info[i].size != -1 &&
chunk_info[i].size != chunk_size)
{
- Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
- chunk_size, chunk_name, filename);
+ Warn("wrong size (%d) of chunk '%s' in level file '%s'",
+ chunk_size, chunk_name, filename);
+
ReadUnusedBytesFromFile(file, chunk_size);
}
else
int chunk_size_expected =
(chunk_info[i].loader)(file, chunk_size, level);
+ if (chunk_size_expected < 0)
+ {
+ Warn("error reading chunk '%s' in level file '%s'",
+ chunk_name, filename);
+
+ break;
+ }
+
// the size of some chunks cannot be checked before reading other
// chunks first (like "HEAD" and "BODY") that contain some header
// information, so check them here
if (chunk_size_expected != chunk_size)
{
- Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
- chunk_size, chunk_name, filename);
+ Warn("wrong size (%d) of chunk '%s' in level file '%s'",
+ chunk_size, chunk_name, filename);
+
+ break;
}
}
}
{ 2, 2 },
};
struct LevelInfo_EM *level_em = level->native_em_level;
- struct LEVEL *lev = level_em->lev;
- struct PLAYER **ply = level_em->ply;
+ struct CAVE *cav = level_em->cav;
int i, j, x, y;
- lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
- lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
+ cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
+ cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
- lev->time_seconds = level->time;
- lev->required_initial = level->gems_needed;
+ cav->time_seconds = level->time;
+ cav->gems_needed = level->gems_needed;
- lev->emerald_score = level->score[SC_EMERALD];
- lev->diamond_score = level->score[SC_DIAMOND];
- lev->alien_score = level->score[SC_ROBOT];
- lev->tank_score = level->score[SC_SPACESHIP];
- lev->bug_score = level->score[SC_BUG];
- lev->eater_score = level->score[SC_YAMYAM];
- lev->nut_score = level->score[SC_NUT];
- lev->dynamite_score = level->score[SC_DYNAMITE];
- lev->key_score = level->score[SC_KEY];
- lev->exit_score = level->score[SC_TIME_BONUS];
+ cav->emerald_score = level->score[SC_EMERALD];
+ cav->diamond_score = level->score[SC_DIAMOND];
+ cav->alien_score = level->score[SC_ROBOT];
+ cav->tank_score = level->score[SC_SPACESHIP];
+ cav->bug_score = level->score[SC_BUG];
+ cav->eater_score = level->score[SC_YAMYAM];
+ cav->nut_score = level->score[SC_NUT];
+ cav->dynamite_score = level->score[SC_DYNAMITE];
+ cav->key_score = level->score[SC_KEY];
+ cav->exit_score = level->score[SC_TIME_BONUS];
+
+ cav->num_eater_arrays = level->num_yamyam_contents;
for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
for (y = 0; y < 3; y++)
for (x = 0; x < 3; x++)
- lev->eater_array[i][y * 3 + x] =
- map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
+ cav->eater_array[i][y * 3 + x] =
+ map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
- lev->amoeba_time = level->amoeba_speed;
- lev->wonderwall_time_initial = level->time_magic_wall;
- lev->wheel_time = level->time_wheel;
+ cav->amoeba_time = level->amoeba_speed;
+ cav->wonderwall_time = level->time_magic_wall;
+ cav->wheel_time = level->time_wheel;
- lev->android_move_time = level->android_move_time;
- lev->android_clone_time = level->android_clone_time;
- lev->ball_random = level->ball_random;
- lev->ball_state_initial = level->ball_state_initial;
- lev->ball_time = level->ball_time;
- lev->num_ball_arrays = level->num_ball_contents;
+ cav->android_move_time = level->android_move_time;
+ cav->android_clone_time = level->android_clone_time;
+ cav->ball_random = level->ball_random;
+ cav->ball_active = level->ball_active_initial;
+ cav->ball_time = level->ball_time;
+ cav->num_ball_arrays = level->num_ball_contents;
- lev->lenses_score = level->lenses_score;
- lev->magnify_score = level->magnify_score;
- lev->slurp_score = level->slurp_score;
+ cav->lenses_score = level->lenses_score;
+ cav->magnify_score = level->magnify_score;
+ cav->slurp_score = level->slurp_score;
- lev->lenses_time = level->lenses_time;
- lev->magnify_time = level->magnify_time;
+ cav->lenses_time = level->lenses_time;
+ cav->magnify_time = level->magnify_time;
- lev->wind_direction_initial =
+ cav->wind_time = 9999;
+ cav->wind_direction =
map_direction_RND_to_EM(level->wind_direction_initial);
- lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
- lev->wind_time : 0);
for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
for (j = 0; j < 8; j++)
- lev->ball_array[i][j] =
- map_element_RND_to_EM(level->
- ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+ cav->ball_array[i][j] =
+ map_element_RND_to_EM_cave(level->ball_content[i].
+ e[ball_xy[j][0]][ball_xy[j][1]]);
map_android_clone_elements_RND_to_EM(level);
- // first fill the complete playfield with the default border element
+ // first fill the complete playfield with the empty space element
for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
- level_em->cave[x][y] = ZBORDER;
-
- if (BorderElement == EL_STEELWALL)
- {
- for (y = 0; y < lev->height + 2; y++)
- for (x = 0; x < lev->width + 2; x++)
- level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
- }
+ cav->cave[x][y] = Cblank;
// then copy the real level contents from level file into the playfield
- for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+ for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
{
- int new_element = map_element_RND_to_EM(level->field[x][y]);
- int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
- int xx = x + 1 + offset;
- int yy = y + 1 + offset;
+ int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
if (level->field[x][y] == EL_AMOEBA_DEAD)
- new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
+ new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
- level_em->cave[xx][yy] = new_element;
+ cav->cave[x][y] = new_element;
}
for (i = 0; i < MAX_PLAYERS; i++)
{
- ply[i]->x_initial = 0;
- ply[i]->y_initial = 0;
+ cav->player_x[i] = -1;
+ cav->player_y[i] = -1;
}
// initialize player positions and delete players from the playfield
- for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+ for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
{
- if (ELEM_IS_PLAYER(level->field[x][y]))
+ if (IS_PLAYER_ELEMENT(level->field[x][y]))
{
int player_nr = GET_PLAYER_NR(level->field[x][y]);
- int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
- int xx = x + 1 + offset;
- int yy = y + 1 + offset;
- ply[player_nr]->x_initial = xx;
- ply[player_nr]->y_initial = yy;
+ cav->player_x[player_nr] = x;
+ cav->player_y[player_nr] = y;
- level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
+ cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
}
}
-
- if (BorderElement == EL_STEELWALL)
- {
- lev->width += 2;
- lev->height += 2;
- }
}
static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
{ 2, 2 },
};
struct LevelInfo_EM *level_em = level->native_em_level;
- struct LEVEL *lev = level_em->lev;
- struct PLAYER **ply = level_em->ply;
+ struct CAVE *cav = level_em->cav;
int i, j, x, y;
- level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
- level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
+ level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
+ level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
- level->time = lev->time_seconds;
- level->gems_needed = lev->required_initial;
+ level->time = cav->time_seconds;
+ level->gems_needed = cav->gems_needed;
sprintf(level->name, "Level %d", level->file_info.nr);
- level->score[SC_EMERALD] = lev->emerald_score;
- level->score[SC_DIAMOND] = lev->diamond_score;
- level->score[SC_ROBOT] = lev->alien_score;
- level->score[SC_SPACESHIP] = lev->tank_score;
- level->score[SC_BUG] = lev->bug_score;
- level->score[SC_YAMYAM] = lev->eater_score;
- level->score[SC_NUT] = lev->nut_score;
- level->score[SC_DYNAMITE] = lev->dynamite_score;
- level->score[SC_KEY] = lev->key_score;
- level->score[SC_TIME_BONUS] = lev->exit_score;
+ level->score[SC_EMERALD] = cav->emerald_score;
+ level->score[SC_DIAMOND] = cav->diamond_score;
+ level->score[SC_ROBOT] = cav->alien_score;
+ level->score[SC_SPACESHIP] = cav->tank_score;
+ level->score[SC_BUG] = cav->bug_score;
+ level->score[SC_YAMYAM] = cav->eater_score;
+ level->score[SC_NUT] = cav->nut_score;
+ level->score[SC_DYNAMITE] = cav->dynamite_score;
+ level->score[SC_KEY] = cav->key_score;
+ level->score[SC_TIME_BONUS] = cav->exit_score;
- level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
+ level->num_yamyam_contents = cav->num_eater_arrays;
- for (i = 0; i < level->num_yamyam_contents; i++)
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
for (y = 0; y < 3; y++)
for (x = 0; x < 3; x++)
level->yamyam_content[i].e[x][y] =
- map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
+ map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
- level->amoeba_speed = lev->amoeba_time;
- level->time_magic_wall = lev->wonderwall_time_initial;
- level->time_wheel = lev->wheel_time;
+ level->amoeba_speed = cav->amoeba_time;
+ level->time_magic_wall = cav->wonderwall_time;
+ level->time_wheel = cav->wheel_time;
- level->android_move_time = lev->android_move_time;
- level->android_clone_time = lev->android_clone_time;
- level->ball_random = lev->ball_random;
- level->ball_state_initial = lev->ball_state_initial;
- level->ball_time = lev->ball_time;
- level->num_ball_contents = lev->num_ball_arrays;
+ level->android_move_time = cav->android_move_time;
+ level->android_clone_time = cav->android_clone_time;
+ level->ball_random = cav->ball_random;
+ level->ball_active_initial = cav->ball_active;
+ level->ball_time = cav->ball_time;
+ level->num_ball_contents = cav->num_ball_arrays;
- level->lenses_score = lev->lenses_score;
- level->magnify_score = lev->magnify_score;
- level->slurp_score = lev->slurp_score;
+ level->lenses_score = cav->lenses_score;
+ level->magnify_score = cav->magnify_score;
+ level->slurp_score = cav->slurp_score;
- level->lenses_time = lev->lenses_time;
- level->magnify_time = lev->magnify_time;
+ level->lenses_time = cav->lenses_time;
+ level->magnify_time = cav->magnify_time;
level->wind_direction_initial =
- map_direction_EM_to_RND(lev->wind_direction_initial);
+ map_direction_EM_to_RND(cav->wind_direction);
for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
for (j = 0; j < 8; j++)
level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
- map_element_EM_to_RND(lev->ball_array[i][j]);
+ map_element_EM_to_RND_cave(cav->ball_array[i][j]);
map_android_clone_elements_EM_to_RND(level);
// convert the playfield (some elements need special treatment)
for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
{
- int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
+ int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
new_element = EL_AMOEBA_DEAD;
{
// in case of all players set to the same field, use the first player
int nr = MAX_PLAYERS - i - 1;
- int jx = ply[nr]->x_initial - 1;
- int jy = ply[nr]->y_initial - 1;
+ int jx = cav->player_x[nr];
+ int jy = cav->player_y[nr];
if (jx != -1 && jy != -1)
level->field[jx][jy] = EL_PLAYER_1 + nr;
}
+
+ // time score is counted for each 10 seconds left in Emerald Mine levels
+ level->time_score_base = 10;
}
{
num_invalid_elements++;
- Error(ERR_DEBUG, "invalid element %d at position %d, %d",
+ Debug("level:native:SP", "invalid element %d at position %d, %d",
element_old, x, y);
}
}
if (num_invalid_elements > 0)
- Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
- (!options.debug ? " (use '--debug' for more details)" : ""));
+ 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] =
if (port_x < 0 || port_x >= level->fieldx ||
port_y < 0 || port_y >= level->fieldy)
{
- Error(ERR_WARN, "special port position (%d, %d) out of bounds",
- port_x, port_y);
+ Warn("special port position (%d, %d) out of bounds", port_x, port_y);
continue;
}
if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
port_element > EL_SP_GRAVITY_PORT_UP)
{
- Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
+ Warn("no special port at position (%d, %d)", port_x, port_y);
continue;
}
level->time_wheel = 0;
level->amoeba_content = EL_EMPTY;
-#if 1
- // original Supaplex does not use score values -- use default values
-#else
+ // original Supaplex does not use score values -- rate by playing time
for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
level->score[i] = 0;
-#endif
+
+ level->rate_time_over_score = TRUE;
// there are no yamyams in supaplex levels
for (i = 0; i < level->num_yamyam_contents; i++)
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);
+ Warn("tape truncated: size exceeds maximum SP demo size %d",
+ SP_MAX_TAPE_LEN);
break;
}
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 };
+ byte action[MAX_TAPE_ACTIONS] = { tape_action };
boolean success = 0;
int j;
if (!success)
{
- Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
- MAX_TAPE_LEN);
+ Warn("SP demo truncated: size exceeds maximum tape size %d",
+ MAX_TAPE_LEN);
break;
}
static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
{
struct LevelInfo_MM *level_mm = level->native_mm_level;
- int x, y;
+ int i, x, y;
level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
level_mm->kettles_needed = level->gems_needed;
level_mm->auto_count_kettles = level->auto_count_gems;
- level_mm->laser_red = level->mm_laser_red;
- level_mm->laser_green = level->mm_laser_green;
- level_mm->laser_blue = level->mm_laser_blue;
+ level_mm->mm_laser_red = level->mm_laser_red;
+ level_mm->mm_laser_green = level->mm_laser_green;
+ level_mm->mm_laser_blue = level->mm_laser_blue;
+
+ level_mm->df_laser_red = level->df_laser_red;
+ level_mm->df_laser_green = level->df_laser_green;
+ level_mm->df_laser_blue = level->df_laser_blue;
strcpy(level_mm->name, level->name);
strcpy(level_mm->author, level->author);
level_mm->time_ball = level->mm_time_ball;
level_mm->time_block = level->mm_time_block;
+ level_mm->num_ball_contents = level->num_mm_ball_contents;
+ level_mm->ball_choice_mode = level->mm_ball_choice_mode;
+ level_mm->rotate_ball_content = level->rotate_mm_ball_content;
+ level_mm->explode_ball = level->explode_mm_ball;
+
+ for (i = 0; i < level->num_mm_ball_contents; i++)
+ level_mm->ball_content[i] =
+ map_element_RND_to_MM(level->mm_ball_content[i]);
+
for (x = 0; x < level->fieldx; x++)
for (y = 0; y < level->fieldy; y++)
Ur[x][y] =
static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
{
struct LevelInfo_MM *level_mm = level->native_mm_level;
- int x, y;
+ int i, x, y;
level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
level->gems_needed = level_mm->kettles_needed;
level->auto_count_gems = level_mm->auto_count_kettles;
- level->mm_laser_red = level_mm->laser_red;
- level->mm_laser_green = level_mm->laser_green;
- level->mm_laser_blue = level_mm->laser_blue;
+ level->mm_laser_red = level_mm->mm_laser_red;
+ level->mm_laser_green = level_mm->mm_laser_green;
+ level->mm_laser_blue = level_mm->mm_laser_blue;
+
+ level->df_laser_red = level_mm->df_laser_red;
+ level->df_laser_green = level_mm->df_laser_green;
+ level->df_laser_blue = level_mm->df_laser_blue;
strcpy(level->name, level_mm->name);
level->mm_time_ball = level_mm->time_ball;
level->mm_time_block = level_mm->time_block;
+ level->num_mm_ball_contents = level_mm->num_ball_contents;
+ level->mm_ball_choice_mode = level_mm->ball_choice_mode;
+ level->rotate_mm_ball_content = level_mm->rotate_ball_content;
+ level->explode_mm_ball = level_mm->explode_ball;
+
+ for (i = 0; i < level->num_mm_ball_contents; i++)
+ level->mm_ball_content[i] =
+ map_element_MM_to_RND(level_mm->ball_content[i]);
+
for (x = 0; x < level->fieldx; x++)
for (y = 0; y < level->fieldy; y++)
level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
break;
case 0x13f5:
- element = EL_YAMYAM;
+ element = EL_YAMYAM_UP;
break;
case 0x1425:
break;
case 0x1682: // secret gate (red)
- element = EL_GATE_1_GRAY;
+ element = EL_EM_GATE_1_GRAY;
break;
case 0x1683: // gate (yellow)
break;
case 0x1684: // secret gate (yellow)
- element = EL_GATE_2_GRAY;
+ element = EL_EM_GATE_2_GRAY;
break;
case 0x1685: // gate (blue)
break;
case 0x1686: // secret gate (blue)
- element = EL_GATE_4_GRAY;
+ element = EL_EM_GATE_4_GRAY;
break;
case 0x1687: // gate (green)
break;
case 0x1688: // secret gate (green)
- element = EL_GATE_3_GRAY;
+ element = EL_EM_GATE_3_GRAY;
break;
case 0x1689: // gate (white)
element = EL_INVISIBLE_SAND;
else
{
- Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
+ Warn("unknown Diamond Caves element 0x%04x", element);
+
element = EL_UNKNOWN;
}
break;
return getMappedElement(element);
}
-static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
- int nr)
+static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
{
byte header[DC_LEVEL_HEADER_SIZE];
int envelope_size;
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "cannot decode level from stream -- using empty level");
+ Warn("cannot decode level from stream -- using empty level");
return;
}
level->extra_time = header[56] | (header[57] << 8);
level->shield_normal_time = header[58] | (header[59] << 8);
+ // shield and extra time elements do not have a score
+ level->score[SC_SHIELD] = 0;
+ level->extra_time_score = 0;
+
+ // set time for normal and deadly shields to the same value
+ level->shield_deadly_time = level->shield_normal_time;
+
// Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
// can slip down from flat walls, like normal walls and steel walls
level->em_slippery_gems = TRUE;
+
+ // time score is counted for each 10 seconds left in Diamond Caves levels
+ level->time_score_base = 10;
}
static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
level->no_valid_file = TRUE;
if (!level_info_only)
- Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+ Warn("cannot read level '%s' -- using empty level", filename);
return;
}
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
- filename);
+ Warn("unknown DC level file '%s' -- using empty level", filename);
return;
}
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
- filename);
+ Warn("cannot fseek in file '%s' -- using empty level", filename);
return;
}
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
- filename);
+ Warn("unknown DC2 level file '%s' -- using empty level", filename);
return;
}
}
- LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
+ LoadLevelFromFileStream_DC(file, level);
closeFile(file);
}
return EL_UNDEFINED;
}
+static void SetLevelSettings_SB(struct LevelInfo *level)
+{
+ // time settings
+ level->time = 0;
+ level->use_step_counter = TRUE;
+
+ // score settings
+ level->score[SC_TIME_BONUS] = 0;
+ level->time_score_base = 1;
+ level->rate_time_over_score = TRUE;
+
+ // game settings
+ level->auto_exit_sokoban = TRUE;
+}
+
static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
struct LevelFileInfo *level_file_info,
boolean level_info_only)
level->no_valid_file = TRUE;
if (!level_info_only)
- Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+ Warn("cannot read level '%s' -- using empty level", filename);
return;
}
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+ Warn("cannot read level '%s' -- using empty level", filename);
return;
}
}
// set special level settings for Sokoban levels
-
- level->time = 0;
- level->use_step_counter = TRUE;
+ SetLevelSettings_SB(level);
if (load_xsb_to_ces)
{
// special global settings can now be set in level template
-
level->use_custom_template = TRUE;
}
}
if (level->game_version < VERSION_IDENT(3,2,0,5))
{
// time bonus score was given for 10 s instead of 1 s before 3.2.0-5
- level->score[SC_TIME_BONUS] /= 10;
+ level->time_score_base = 10;
}
if (leveldir_current->latest_engine)
// only Sokoban fields (but not objects) had to be solved before 4.1.1.1
if (level->game_version < VERSION_IDENT(4,1,1,1))
level->sb_objects_needed = FALSE;
+
+ // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
+ if (level->game_version <= VERSION_IDENT(4,2,2,0))
+ level->finish_dig_collect = FALSE;
+
+ // CE changing to player was kept under the player if walkable up to 4.2.3.1
+ if (level->game_version <= VERSION_IDENT(4,2,3,1))
+ level->keep_walkable_ce = TRUE;
+}
+
+static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
+{
+ boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
+ int x, y;
+
+ // check if this level is (not) a Sokoban level
+ for (y = 0; y < level->fieldy; y++)
+ for (x = 0; x < level->fieldx; x++)
+ if (!IS_SB_ELEMENT(Tile[x][y]))
+ is_sokoban_level = FALSE;
+
+ if (is_sokoban_level)
+ {
+ // set special level settings for Sokoban levels
+ SetLevelSettings_SB(level);
+ }
+}
+
+static void LoadLevel_InitSettings(struct LevelInfo *level)
+{
+ // adjust level settings for (non-native) Sokoban-style levels
+ LoadLevel_InitSettings_SB(level);
+
+ // rename levels with title "nameless level" or if renaming is forced
+ if (leveldir_current->empty_level_name != NULL &&
+ (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
+ leveldir_current->force_level_name))
+ snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
+ leveldir_current->empty_level_name, level_nr);
}
static void LoadLevel_InitStandardElements(struct LevelInfo *level)
element_info[element].ignition_delay = 8;
}
}
+
+ // set mouse click change events to work for left/middle/right mouse button
+ if (level->game_version < VERSION_IDENT(4,2,3,0))
+ {
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+ struct ElementInfo *ei = &element_info[element];
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change = &ei->change_page[j];
+
+ if (change->has_event[CE_CLICKED_BY_MOUSE] ||
+ change->has_event[CE_PRESSED_BY_MOUSE] ||
+ change->has_event[CE_MOUSE_CLICKED_ON_X] ||
+ change->has_event[CE_MOUSE_PRESSED_ON_X])
+ change->trigger_side = CH_SIDE_ANY;
+ }
+ }
+ }
}
static void LoadLevel_InitElements(struct LevelInfo *level)
// copy elements to runtime playfield array
for (x = 0; x < MAX_LEV_FIELDX; x++)
for (y = 0; y < MAX_LEV_FIELDY; y++)
- Feld[x][y] = level->field[x][y];
+ Tile[x][y] = level->field[x][y];
// initialize level size variables for faster access
lev_fieldx = level->fieldx;
LoadLevel_InitVersion(&level_template);
LoadLevel_InitElements(&level_template);
+ LoadLevel_InitSettings(&level_template);
ActivateLevelTemplate();
}
{
if (!fileExists(getGlobalLevelTemplateFilename()))
{
- Error(ERR_WARN, "no level template found for this level");
+ Warn("no level template found for this level");
return;
}
LoadLevel_InitVersion(&level);
LoadLevel_InitElements(&level);
LoadLevel_InitPlayfield(&level);
+ LoadLevel_InitSettings(&level);
LoadLevel_InitNativeEngines(&level);
}
int chunk_size = 0;
int x, y;
- for (y = 0; y < level->fieldy; y++)
- for (x = 0; x < level->fieldx; x++)
+ for (y = 0; y < level->fieldy; y++)
+ for (x = 0; x < level->fieldx; x++)
if (level->encoding_16bit_field)
chunk_size += putFile16BitBE(file, level->field[x][y]);
else
// chunk header already written -- write empty chunk data
WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
- Error(ERR_WARN, "cannot save content for element '%d'", element);
+ Warn("cannot save content for element '%d'", element);
+
return;
}
}
if (check != num_changed_custom_elements) // should not happen
- Error(ERR_WARN, "inconsistent number of custom element properties");
+ Warn("inconsistent number of custom element properties");
}
#endif
}
if (check != num_changed_custom_elements) // should not happen
- Error(ERR_WARN, "inconsistent number of custom target elements");
+ Warn("inconsistent number of custom target elements");
}
#endif
}
if (check != num_changed_custom_elements) // should not happen
- Error(ERR_WARN, "inconsistent number of custom element properties");
+ Warn("inconsistent number of custom element properties");
}
#endif
event_bits = 0;
for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
if (change->has_event[j])
- event_bits |= (1 << j);
+ event_bits |= (1u << j);
putFile32BitBE(file, event_bits);
putFile16BitBE(file, change->target_element);
event_bits = 0;
for (j = 32; j < NUM_CHANGE_EVENTS; j++)
if (change->has_event[j])
- event_bits |= (1 << (j - 32));
+ event_bits |= (1u << (j - 32));
putFile8Bit(file, event_bits);
}
}
return chunk_size;
}
+static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
+{
+ struct ElementInfo *ei = &element_info[element];
+ int chunk_size = 0;
+ int i;
+
+ chunk_size += putFile16BitBE(file, element);
+
+ xx_ei = *ei; // copy element data into temporary buffer
+
+ for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
+ chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
+
+ return chunk_size;
+}
+
static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
boolean save_as_template)
{
if (!(file = fopen(filename, MODE_WRITE)))
{
- Error(ERR_WARN, "cannot save level file '%s'", filename);
+ Warn("cannot save level file '%s'", filename);
+
return;
}
SaveLevel_GRPX(file, level, element);
}
}
+
+ for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
+ {
+ int element = GET_EMPTY_ELEMENT(i);
+
+ chunk_size = SaveLevel_EMPX(NULL, level, element);
+ if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
+ {
+ putFileChunkBE(file, "EMPX", chunk_size);
+ SaveLevel_EMPX(file, level, element);
+ }
+ }
}
fclose(file);
{
if (level->no_level_file || level->no_valid_file)
{
- Error(ERR_WARN, "cannot dump -- no valid level file found");
+ Warn("cannot dump -- no valid level file found");
return;
}
Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
+ Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
+
+ if (options.debug)
+ {
+ int i, j;
+
+ for (i = 0; i < NUM_ENVELOPES; i++)
+ {
+ char *text = level->envelope[i].text;
+ int text_len = strlen(text);
+ boolean has_text = FALSE;
+
+ for (j = 0; j < text_len; j++)
+ if (text[j] != ' ' && text[j] != '\n')
+ has_text = TRUE;
+
+ if (has_text)
+ {
+ Print("\n");
+ Print("Envelope %d:\n'%s'\n", i + 1, text);
+ }
+ }
+ }
PrintLine("-", 79);
}
+void DumpLevels(void)
+{
+ static LevelDirTree *dumplevel_leveldir = NULL;
+
+ dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
+ global.dumplevel_leveldir);
+
+ if (dumplevel_leveldir == NULL)
+ Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
+
+ if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
+ global.dumplevel_level_nr > dumplevel_leveldir->last_level)
+ Fail("no such level number: %d", global.dumplevel_level_nr);
+
+ leveldir_current = dumplevel_leveldir;
+
+ LoadLevel(global.dumplevel_level_nr);
+ DumpLevel(&level);
+
+ CloseAllAndExit(0);
+}
+
// ============================================================================
// tape file functions
// at least one (default: the first) player participates in every tape
tape.num_participating_players = 1;
+ tape.property_bits = TAPE_PROPERTY_NONE;
+
tape.level_nr = level_nr;
tape.counter = 0;
tape.changed = FALSE;
+ tape.solved = FALSE;
tape.recording = FALSE;
tape.playing = FALSE;
tape.pausing = FALSE;
+ tape.scr_fieldx = SCR_FIELDX_DEFAULT;
+ tape.scr_fieldy = SCR_FIELDY_DEFAULT;
+
+ tape.no_info_chunk = TRUE;
tape.no_valid_file = FALSE;
}
+static int getTapePosSize(struct TapeInfo *tape)
+{
+ int tape_pos_size = 0;
+
+ if (tape->use_key_actions)
+ tape_pos_size += tape->num_participating_players;
+
+ if (tape->use_mouse_actions)
+ tape_pos_size += 3; // x and y position and mouse button mask
+
+ tape_pos_size += 1; // tape action delay value
+
+ return tape_pos_size;
+}
+
+static void setTapeActionFlags(struct TapeInfo *tape, int value)
+{
+ tape->use_key_actions = FALSE;
+ tape->use_mouse_actions = FALSE;
+
+ if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
+ tape->use_key_actions = TRUE;
+
+ if (value != TAPE_USE_KEY_ACTIONS_ONLY)
+ tape->use_mouse_actions = TRUE;
+}
+
+static int getTapeActionValue(struct TapeInfo *tape)
+{
+ return (tape->use_key_actions &&
+ tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
+ tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
+ tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
+ TAPE_ACTIONS_DEFAULT);
+}
+
static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
{
tape->file_version = getFileVersion(file);
}
}
- tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+ setTapeActionFlags(tape, getFile8Bit(file));
- ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
+ tape->property_bits = getFile8Bit(file);
+ tape->solved = getFile8Bit(file);
engine_version = getFileVersion(file);
if (engine_version > 0)
return chunk_size;
}
+static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
+{
+ tape->scr_fieldx = getFile8Bit(file);
+ tape->scr_fieldy = getFile8Bit(file);
+
+ return chunk_size;
+}
+
static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
{
+ char *level_identifier = NULL;
int level_identifier_size;
int i;
+ tape->no_info_chunk = FALSE;
+
level_identifier_size = getFile16BitBE(file);
- tape->level_identifier =
- checked_realloc(tape->level_identifier, level_identifier_size);
+ level_identifier = checked_malloc(level_identifier_size);
for (i = 0; i < level_identifier_size; i++)
- tape->level_identifier[i] = getFile8Bit(file);
+ level_identifier[i] = getFile8Bit(file);
+
+ strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
+ tape->level_identifier[MAX_FILENAME_LEN] = '\0';
+
+ checked_free(level_identifier);
tape->level_nr = getFile16BitBE(file);
static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
{
int i, j;
- int tape_pos_size =
- (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
+ int tape_pos_size = getTapePosSize(tape);
int chunk_size_expected = tape_pos_size * tape->length;
if (chunk_size_expected != chunk_size)
{
if (i >= MAX_TAPE_LEN)
{
- Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
+ 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);
+ ReadUnusedBytesFromFile(file, tape_pos_size);
break;
}
- if (tape->use_mouse)
- {
- tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
- tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
- tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
-
- tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
- }
- else
+ if (tape->use_key_actions)
{
for (j = 0; j < MAX_PLAYERS; j++)
{
}
}
+ if (tape->use_mouse_actions)
+ {
+ tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
+ tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
+ tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
+ }
+
tape->pos[i].delay = getFile8Bit(file);
if (tape->file_version == FILE_VERSION_1_0)
default:
tape.no_valid_file = TRUE;
- Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
+ Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
break;
}
{
tape.no_valid_file = TRUE;
- Error(ERR_WARN, "unknown format of tape file '%s'", filename);
+ Warn("unknown format of tape file '%s'", filename);
closeFile(file);
{
tape.no_valid_file = TRUE;
- Error(ERR_WARN, "unknown format of tape file '%s'", filename);
+ Warn("unknown format of tape file '%s'", filename);
closeFile(file);
{
tape.no_valid_file = TRUE;
- Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
+ Warn("unsupported version of tape file '%s'", filename);
closeFile(file);
{
{ "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
{ "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
+ { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
{ "INFO", -1, LoadTape_INFO },
{ "BODY", -1, LoadTape_BODY },
{ NULL, 0, NULL }
if (chunk_info[i].name == NULL)
{
- Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
+ Warn("unknown chunk '%s' in tape file '%s'",
chunk_name, filename);
+
ReadUnusedBytesFromFile(file, chunk_size);
}
else if (chunk_info[i].size != -1 &&
chunk_info[i].size != chunk_size)
{
- Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
+ Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
chunk_size, chunk_name, filename);
+
ReadUnusedBytesFromFile(file, chunk_size);
}
else
// information, so check them here
if (chunk_size_expected != chunk_size)
{
- Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
+ Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
chunk_size, chunk_name, filename);
}
}
tape.length_seconds = GetTapeLengthSeconds();
#if 0
- printf("::: tape file version: %d\n", tape.file_version);
- printf("::: tape game version: %d\n", tape.game_version);
- printf("::: tape engine version: %d\n", tape.engine_version);
+ Debug("files:LoadTapeFromFilename", "tape file version: %d",
+ tape.file_version);
+ Debug("files:LoadTapeFromFilename", "tape game version: %d",
+ tape.game_version);
+ Debug("files:LoadTapeFromFilename", "tape engine version: %d",
+ tape.engine_version);
#endif
}
CopyNativeTape_SP_to_RND(&level);
}
+void LoadScoreTape(char *score_tape_basename, int nr)
+{
+ char *filename = getScoreTapeFilename(score_tape_basename, nr);
+
+ LoadTapeFromFilename(filename);
+}
+
+void LoadScoreCacheTape(char *score_tape_basename, int nr)
+{
+ char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
+
+ LoadTapeFromFilename(filename);
+}
+
+static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
+{
+ // chunk required for team mode tapes with non-default screen size
+ return (tape->num_participating_players > 1 &&
+ (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
+ tape->scr_fieldy != SCR_FIELDY_DEFAULT));
+}
+
static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
{
putFileVersion(file, tape->file_version);
putFile8Bit(file, store_participating_players);
- putFile8Bit(file, (tape->use_mouse ? 1 : 0));
+ putFile8Bit(file, getTapeActionValue(tape));
- // unused bytes not at the end here for 4-byte alignment of engine_version
- WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
+ putFile8Bit(file, tape->property_bits);
+ putFile8Bit(file, tape->solved);
putFileVersion(file, tape->engine_version);
}
+static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
+{
+ putFile8Bit(file, tape->scr_fieldx);
+ putFile8Bit(file, tape->scr_fieldy);
+}
+
static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
{
int level_identifier_size = strlen(tape->level_identifier) + 1;
for (i = 0; i < tape->length; i++)
{
- if (tape->use_mouse)
- {
- putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
- putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
- putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
- }
- else
+ if (tape->use_key_actions)
{
for (j = 0; j < MAX_PLAYERS; j++)
if (tape->player_participates[j])
putFile8Bit(file, tape->pos[i].action[j]);
}
+ if (tape->use_mouse_actions)
+ {
+ putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
+ putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
+ putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
+ }
+
putFile8Bit(file, tape->pos[i].delay);
}
}
-void SaveTape(int nr)
+void SaveTapeToFilename(char *filename)
{
- char *filename = getTapeFilename(nr);
FILE *file;
- int num_participating_players = 0;
int tape_pos_size;
int info_chunk_size;
int body_chunk_size;
- int i;
-
- InitTapeDirectory(leveldir_current->subdir);
if (!(file = fopen(filename, MODE_WRITE)))
{
- Error(ERR_WARN, "cannot save level recording file '%s'", filename);
+ Warn("cannot save level recording file '%s'", filename);
+
return;
}
- tape.file_version = FILE_VERSION_ACTUAL;
- tape.game_version = GAME_VERSION_ACTUAL;
-
- // count number of participating players
- for (i = 0; i < MAX_PLAYERS; i++)
- if (tape.player_participates[i])
- num_participating_players++;
-
- tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
+ tape_pos_size = getTapePosSize(&tape);
info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
body_chunk_size = tape_pos_size * tape.length;
putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
SaveTape_HEAD(file, &tape);
+ if (checkSaveTape_SCRN(&tape))
+ {
+ putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
+ SaveTape_SCRN(file, &tape);
+ }
+
putFileChunkBE(file, "INFO", info_chunk_size);
SaveTape_INFO(file, &tape);
fclose(file);
SetFilePermissions(filename, PERMS_PRIVATE);
+}
+
+static void SaveTapeExt(char *filename)
+{
+ int i;
+
+ tape.file_version = FILE_VERSION_ACTUAL;
+ tape.game_version = GAME_VERSION_ACTUAL;
+
+ tape.num_participating_players = 0;
+
+ // count number of participating players
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (tape.player_participates[i])
+ tape.num_participating_players++;
+
+ SaveTapeToFilename(filename);
tape.changed = FALSE;
}
+void SaveTape(int nr)
+{
+ char *filename = getTapeFilename(nr);
+
+ InitTapeDirectory(leveldir_current->subdir);
+
+ SaveTapeExt(filename);
+}
+
+void SaveScoreTape(int nr)
+{
+ char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
+
+ // used instead of "leveldir_current->subdir" (for network games)
+ InitScoreTapeDirectory(levelset.identifier, nr);
+
+ SaveTapeExt(filename);
+}
+
static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
unsigned int req_state_added)
{
if (tape->no_valid_file)
{
- Error(ERR_WARN, "cannot dump -- no valid tape file found");
+ Warn("cannot dump -- no valid tape file found");
return;
}
PrintLine("-", 79);
+
Print("Tape of Level %03d (file version %08d, game version %08d)\n",
tape->level_nr, tape->file_version, tape->game_version);
Print(" (effective engine version %08d)\n",
tape->engine_version);
Print("Level series identifier: '%s'\n", tape->level_identifier);
+
+ Print("Solution tape: %s\n",
+ tape->solved ? "yes" :
+ tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
+
+ Print("Special tape properties: ");
+ if (tape->property_bits == TAPE_PROPERTY_NONE)
+ Print("[none]");
+ if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
+ Print("[em_random_bug]");
+ if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
+ Print("[game_speed]");
+ if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
+ Print("[pause]");
+ if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
+ Print("[single_step]");
+ if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
+ Print("[snapshot]");
+ if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
+ Print("[replayed]");
+ if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
+ Print("[tas_keys]");
+ if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
+ Print("[small_graphics]");
+ Print("\n");
+
+ int year2 = tape->date / 10000;
+ int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
+ int month_index_raw = (tape->date / 100) % 100;
+ int month_index = month_index_raw % 12; // prevent invalid index
+ int month = month_index + 1;
+ int day = tape->date % 100;
+
+ Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
+
PrintLine("-", 79);
tape_frame_counter = 0;
PrintLine("-", 79);
}
+void DumpTapes(void)
+{
+ static LevelDirTree *dumptape_leveldir = NULL;
+
+ dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
+ global.dumptape_leveldir);
+
+ if (dumptape_leveldir == NULL)
+ Fail("no such level identifier: '%s'", global.dumptape_leveldir);
+
+ if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
+ global.dumptape_level_nr > dumptape_leveldir->last_level)
+ Fail("no such level number: %d", global.dumptape_level_nr);
+
+ leveldir_current = dumptape_leveldir;
+
+ if (options.mytapes)
+ LoadTape(global.dumptape_level_nr);
+ else
+ LoadSolutionTape(global.dumptape_level_nr);
+
+ DumpTape(&tape);
+
+ CloseAllAndExit(0);
+}
+
// ============================================================================
// score file functions
// ============================================================================
-void LoadScore(int nr)
+static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
+{
+ int i;
+
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
+ {
+ strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
+ strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
+ scores->entry[i].score = 0;
+ scores->entry[i].time = 0;
+
+ scores->entry[i].id = -1;
+ strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
+ strcpy(scores->entry[i].platform, UNKNOWN_NAME);
+ strcpy(scores->entry[i].version, UNKNOWN_NAME);
+ strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
+ strcpy(scores->entry[i].country_code, "??");
+ }
+
+ scores->num_entries = 0;
+ scores->last_added = -1;
+ scores->last_added_local = -1;
+
+ scores->updated = FALSE;
+ scores->uploaded = FALSE;
+ scores->tape_downloaded = FALSE;
+ scores->force_last_added = FALSE;
+
+ // The following values are intentionally not reset here:
+ // - last_level_nr
+ // - last_entry_nr
+ // - next_level_nr
+ // - continue_playing
+ // - continue_on_return
+}
+
+static void setScoreInfoToDefaults(void)
+{
+ setScoreInfoToDefaultsExt(&scores);
+}
+
+static void setServerScoreInfoToDefaults(void)
+{
+ setScoreInfoToDefaultsExt(&server_scores);
+}
+
+static void LoadScore_OLD(int nr)
{
int i;
char *filename = getScoreFilename(nr);
char *line_ptr;
FILE *file;
- // always start with reliable default values
- for (i = 0; i < MAX_SCORE_ENTRIES; i++)
- {
- strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
- highscore[i].Score = 0;
- }
-
if (!(file = fopen(filename, MODE_READ)))
return;
if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
cookie[strlen(cookie) - 1] = '\0';
- if (!checkCookieString(cookie, SCORE_COOKIE))
+ if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
{
- Error(ERR_WARN, "unknown format of score file '%s'", filename);
+ Warn("unknown format of score file '%s'", filename);
+
fclose(file);
+
return;
}
for (i = 0; i < MAX_SCORE_ENTRIES; i++)
{
- if (fscanf(file, "%d", &highscore[i].Score) == EOF)
- Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
+ if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
+ Warn("fscanf() failed; %s", strerror(errno));
+
if (fgets(line, MAX_LINE_LEN, file) == NULL)
line[0] = '\0';
{
if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
{
- strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
- highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
+ strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
+ scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
break;
}
}
fclose(file);
}
-void SaveScore(int nr)
+static void ConvertScore_OLD(void)
{
- int i;
- int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
- char *filename = getScoreFilename(nr);
- FILE *file;
+ // only convert score to time for levels that rate playing time over score
+ if (!level.rate_time_over_score)
+ return;
- // used instead of "leveldir_current->subdir" (for network games)
- InitScoreDirectory(levelset.identifier);
+ // convert old score to playing time for score-less levels (like Supaplex)
+ int time_final_max = 999;
+ int i;
- if (!(file = fopen(filename, MODE_WRITE)))
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
{
- Error(ERR_WARN, "cannot save score for level %d", nr);
- return;
+ int score = scores.entry[i].score;
+
+ if (score > 0 && score < time_final_max)
+ scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
}
+}
- fprintf(file, "%s\n\n", SCORE_COOKIE);
+static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
+{
+ scores->file_version = getFileVersion(file);
+ scores->game_version = getFileVersion(file);
- for (i = 0; i < MAX_SCORE_ENTRIES; i++)
- fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
+ return chunk_size;
+}
- fclose(file);
+static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
+{
+ char *level_identifier = NULL;
+ int level_identifier_size;
+ int i;
- SetFilePermissions(filename, permissions);
-}
+ level_identifier_size = getFile16BitBE(file);
+ level_identifier = checked_malloc(level_identifier_size);
-// ============================================================================
-// setup file functions
-// ============================================================================
+ for (i = 0; i < level_identifier_size; i++)
+ level_identifier[i] = getFile8Bit(file);
-#define TOKEN_STR_PLAYER_PREFIX "player_"
+ strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
+ scores->level_identifier[MAX_FILENAME_LEN] = '\0';
+ checked_free(level_identifier);
-static struct SetupInfo si;
-static struct SetupAutoSetupInfo sasi;
-static struct SetupEditorInfo sei;
-static struct SetupEditorCascadeInfo seci;
-static struct SetupShortcutInfo ssi;
-static struct SetupInputInfo sii;
-static struct SetupSystemInfo syi;
-static struct SetupInternalInfo sxi;
-static struct SetupDebugInfo sdi;
-static struct OptionInfo soi;
+ scores->level_nr = getFile16BitBE(file);
+ scores->num_entries = getFile16BitBE(file);
-static struct TokenInfo global_setup_tokens[] =
-{
- { TYPE_STRING, &si.player_name, "player_name" },
- { TYPE_SWITCH, &si.sound, "sound" },
- { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
- { TYPE_SWITCH, &si.sound_music, "background_music" },
- { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
- { TYPE_SWITCH, &si.toons, "toons" },
- { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
- { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
- { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
- { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
- { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
- { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
- { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
- { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
- { 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.auto_play_next_level, "auto_play_next_level" },
- { TYPE_SWITCH, &si.skip_scores_after_game, "skip_scores_after_game" },
- { TYPE_SWITCH, &si.time_limit, "time_limit" },
- { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
- { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
- { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
- { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
- { TYPE_STRING, &si.vsync_mode, "vsync_mode" },
- { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
- { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
- { TYPE_SWITCH, &si.ask_on_game_over, "ask_on_game_over" },
- { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
- { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
- { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
- { TYPE_SWITCH, &si.game_speed_extended, "game_speed_extended" },
- { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
- { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
- { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
- { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
- { TYPE_STRING, &si.graphics_set, "graphics_set" },
- { TYPE_STRING, &si.sounds_set, "sounds_set" },
- { TYPE_STRING, &si.music_set, "music_set" },
- { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
- { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
- { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
- { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
- { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
- { TYPE_INTEGER,&si.volume_music, "volume_music" },
- { TYPE_SWITCH, &si.network_mode, "network_mode" },
- { TYPE_PLAYER, &si.network_player_nr, "network_player" },
- { TYPE_STRING, &si.network_server_hostname, "network_server_hostname" },
- { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
- { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
- { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
- { TYPE_INTEGER,&si.touch.transparency, "touch.transparency" },
- { TYPE_INTEGER,&si.touch.draw_outlined, "touch.draw_outlined" },
- { TYPE_INTEGER,&si.touch.draw_pressed, "touch.draw_pressed" },
- { TYPE_INTEGER,&si.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize" },
- { TYPE_INTEGER,&si.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize" },
- { TYPE_INTEGER,&si.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize" },
- { TYPE_INTEGER,&si.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize" },
-};
+ chunk_size = 2 + level_identifier_size + 2 + 2;
-static struct TokenInfo auto_setup_tokens[] =
-{
- { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
-};
+ return chunk_size;
+}
-static struct TokenInfo editor_setup_tokens[] =
+static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
{
- { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
- { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
- { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
- { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
- { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
- { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
-};
+ int i, j;
-static struct TokenInfo editor_cascade_setup_tokens[] =
-{
- { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
- { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
- { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
- { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
- { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
- { 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" },
- { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
- { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
- { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
- { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
-};
+ for (i = 0; i < scores->num_entries; i++)
+ {
+ for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
+ scores->entry[i].name[j] = getFile8Bit(file);
-static struct TokenInfo shortcut_setup_tokens[] =
-{
- { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
- { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
- { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
- { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
- { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
- { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
- { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
- { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
- { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
- { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
- { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
- { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
- { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
- { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
- { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
- { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
- { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
- { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
- { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
- { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
- { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
-};
+ scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
+ }
-static struct TokenInfo player_setup_tokens[] =
-{
- { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
- { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
- { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
- { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
- { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
- { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
- { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
- { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
- { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
- { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
- { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
- { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
- { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
- { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
- { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
- { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
-};
+ chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
-static struct TokenInfo system_setup_tokens[] =
-{
- { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
- { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
- { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
-};
+ return chunk_size;
+}
-static struct TokenInfo internal_setup_tokens[] =
+static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
{
- { 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.program_copyright, "program_copyright" },
- { TYPE_STRING, &sxi.program_company, "program_company" },
- { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
- { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
- { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
- { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
- { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
- { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
- { 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" },
-};
+ int i;
-static struct TokenInfo debug_setup_tokens[] =
-{
- { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
- { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
- { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
- { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
- { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
- { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
- { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
- { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
- { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
- { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
- { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
- { 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" },
-};
+ for (i = 0; i < scores->num_entries; i++)
+ scores->entry[i].score = getFile16BitBE(file);
-static struct TokenInfo options_setup_tokens[] =
-{
- { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
-};
+ chunk_size = scores->num_entries * 2;
-static char *get_corrected_login_name(char *login_name)
+ return chunk_size;
+}
+
+static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
{
- // needed because player name must be a fixed length string
- char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
+ int i;
- strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
- login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
+ for (i = 0; i < scores->num_entries; i++)
+ scores->entry[i].score = getFile32BitBE(file);
- if (strlen(login_name) > MAX_PLAYER_NAME_LEN) // name has been cut
- if (strchr(login_name_new, ' '))
- *strchr(login_name_new, ' ') = '\0';
+ chunk_size = scores->num_entries * 4;
- return login_name_new;
+ return chunk_size;
}
-static void setSetupInfoToDefaults(struct SetupInfo *si)
+static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
{
int i;
- si->player_name = get_corrected_login_name(getLoginName());
+ for (i = 0; i < scores->num_entries; i++)
+ scores->entry[i].time = getFile32BitBE(file);
- si->sound = TRUE;
- si->sound_loops = TRUE;
- si->sound_music = TRUE;
- si->sound_simple = TRUE;
- si->toons = TRUE;
- si->scroll_delay = TRUE;
- si->scroll_delay_value = STD_SCROLL_DELAY;
- si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
- si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
- si->fade_screens = TRUE;
- si->autorecord = TRUE;
- si->show_titlescreen = TRUE;
- si->quick_doors = FALSE;
- si->team_mode = FALSE;
- si->handicap = TRUE;
- si->skip_levels = TRUE;
- si->increment_levels = TRUE;
- si->auto_play_next_level = TRUE;
- si->skip_scores_after_game = FALSE;
- si->time_limit = TRUE;
- si->fullscreen = FALSE;
- si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
- si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
- si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
- si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
- si->ask_on_escape = TRUE;
- si->ask_on_escape_editor = TRUE;
- si->ask_on_game_over = TRUE;
- si->quick_switch = FALSE;
- si->input_on_focus = FALSE;
- si->prefer_aga_graphics = TRUE;
- si->game_speed_extended = FALSE;
- si->game_frame_delay = GAME_FRAME_DELAY;
- si->sp_show_border_elements = FALSE;
- si->small_game_graphics = FALSE;
- si->show_snapshot_buttons = FALSE;
+ chunk_size = scores->num_entries * 4;
- si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
- si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
- si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
+ return chunk_size;
+}
- si->override_level_graphics = FALSE;
- si->override_level_sounds = FALSE;
- si->override_level_music = FALSE;
+static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
+{
+ int i, j;
- si->volume_simple = 100; // percent
- si->volume_loops = 100; // percent
- si->volume_music = 100; // percent
+ for (i = 0; i < scores->num_entries; i++)
+ {
+ for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
+ scores->entry[i].tape_basename[j] = getFile8Bit(file);
- si->network_mode = FALSE;
- si->network_player_nr = 0; // first player
- si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
+ scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
+ }
- si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
- si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
+ chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
+
+ return chunk_size;
+}
+
+void LoadScore(int nr)
+{
+ char *filename = getScoreFilename(nr);
+ char cookie[MAX_LINE_LEN];
+ char chunk_name[CHUNK_ID_LEN + 1];
+ int chunk_size;
+ boolean old_score_file_format = FALSE;
+ File *file;
+
+ // always start with reliable default values
+ setScoreInfoToDefaults();
+
+ if (!(file = openFile(filename, MODE_READ)))
+ return;
+
+ getFileChunkBE(file, chunk_name, NULL);
+ if (strEqual(chunk_name, "RND1"))
+ {
+ getFile32BitBE(file); // not used
+
+ getFileChunkBE(file, chunk_name, NULL);
+ if (!strEqual(chunk_name, "SCOR"))
+ {
+ Warn("unknown format of score file '%s'", filename);
+
+ closeFile(file);
+
+ return;
+ }
+ }
+ else // check for old file format with cookie string
+ {
+ strcpy(cookie, chunk_name);
+ if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
+ cookie[4] = '\0';
+ if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
+ cookie[strlen(cookie) - 1] = '\0';
+
+ if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
+ {
+ Warn("unknown format of score file '%s'", filename);
+
+ closeFile(file);
+
+ return;
+ }
+
+ old_score_file_format = TRUE;
+ }
+
+ if (old_score_file_format)
+ {
+ // score files from versions before 4.2.4.0 without chunk structure
+ LoadScore_OLD(nr);
+
+ // convert score to time, if possible (mainly for Supaplex levels)
+ ConvertScore_OLD();
+ }
+ else
+ {
+ static struct
+ {
+ char *name;
+ int size;
+ int (*loader)(File *, int, struct ScoreInfo *);
+ }
+ chunk_info[] =
+ {
+ { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
+ { "INFO", -1, LoadScore_INFO },
+ { "NAME", -1, LoadScore_NAME },
+ { "SCOR", -1, LoadScore_SCOR },
+ { "SC4R", -1, LoadScore_SC4R },
+ { "TIME", -1, LoadScore_TIME },
+ { "TAPE", -1, LoadScore_TAPE },
+
+ { NULL, 0, NULL }
+ };
+
+ while (getFileChunkBE(file, chunk_name, &chunk_size))
+ {
+ int i = 0;
+
+ while (chunk_info[i].name != NULL &&
+ !strEqual(chunk_name, chunk_info[i].name))
+ i++;
+
+ if (chunk_info[i].name == NULL)
+ {
+ Warn("unknown chunk '%s' in score file '%s'",
+ chunk_name, filename);
+
+ ReadUnusedBytesFromFile(file, chunk_size);
+ }
+ else if (chunk_info[i].size != -1 &&
+ chunk_info[i].size != chunk_size)
+ {
+ Warn("wrong size (%d) of chunk '%s' in score file '%s'",
+ chunk_size, chunk_name, filename);
+
+ ReadUnusedBytesFromFile(file, chunk_size);
+ }
+ else
+ {
+ // call function to load this score chunk
+ int chunk_size_expected =
+ (chunk_info[i].loader)(file, chunk_size, &scores);
+
+ // the size of some chunks cannot be checked before reading other
+ // chunks first (like "HEAD" and "BODY") that contain some header
+ // information, so check them here
+ if (chunk_size_expected != chunk_size)
+ {
+ Warn("wrong size (%d) of chunk '%s' in score file '%s'",
+ chunk_size, chunk_name, filename);
+ }
+ }
+ }
+ }
+
+ closeFile(file);
+}
+
+#if ENABLE_HISTORIC_CHUNKS
+void SaveScore_OLD(int nr)
+{
+ int i;
+ char *filename = getScoreFilename(nr);
+ FILE *file;
+
+ // used instead of "leveldir_current->subdir" (for network games)
+ InitScoreDirectory(levelset.identifier);
+
+ if (!(file = fopen(filename, MODE_WRITE)))
+ {
+ Warn("cannot save score for level %d", nr);
+
+ return;
+ }
+
+ fprintf(file, "%s\n\n", SCORE_COOKIE);
+
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
+ fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
+
+ fclose(file);
+
+ SetFilePermissions(filename, PERMS_PRIVATE);
+}
+#endif
+
+static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
+{
+ putFileVersion(file, scores->file_version);
+ putFileVersion(file, scores->game_version);
+}
+
+static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
+{
+ int level_identifier_size = strlen(scores->level_identifier) + 1;
+ int i;
+
+ putFile16BitBE(file, level_identifier_size);
+
+ for (i = 0; i < level_identifier_size; i++)
+ putFile8Bit(file, scores->level_identifier[i]);
+
+ putFile16BitBE(file, scores->level_nr);
+ putFile16BitBE(file, scores->num_entries);
+}
+
+static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
+{
+ int i, j;
+
+ for (i = 0; i < scores->num_entries; i++)
+ {
+ int name_size = strlen(scores->entry[i].name);
+
+ for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
+ putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
+ }
+}
+
+static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
+{
+ int i;
+
+ for (i = 0; i < scores->num_entries; i++)
+ putFile16BitBE(file, scores->entry[i].score);
+}
+
+static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
+{
+ int i;
+
+ for (i = 0; i < scores->num_entries; i++)
+ putFile32BitBE(file, scores->entry[i].score);
+}
+
+static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
+{
+ int i;
+
+ for (i = 0; i < scores->num_entries; i++)
+ putFile32BitBE(file, scores->entry[i].time);
+}
+
+static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
+{
+ int i, j;
+
+ for (i = 0; i < scores->num_entries; i++)
+ {
+ int size = strlen(scores->entry[i].tape_basename);
+
+ for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
+ putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
+ }
+}
+
+static void SaveScoreToFilename(char *filename)
+{
+ FILE *file;
+ int info_chunk_size;
+ int name_chunk_size;
+ int scor_chunk_size;
+ int sc4r_chunk_size;
+ int time_chunk_size;
+ int tape_chunk_size;
+ boolean has_large_score_values;
+ int i;
+
+ if (!(file = fopen(filename, MODE_WRITE)))
+ {
+ Warn("cannot save score file '%s'", filename);
+
+ return;
+ }
+
+ info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
+ name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
+ scor_chunk_size = scores.num_entries * 2;
+ sc4r_chunk_size = scores.num_entries * 4;
+ time_chunk_size = scores.num_entries * 4;
+ tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
+
+ has_large_score_values = FALSE;
+ for (i = 0; i < scores.num_entries; i++)
+ if (scores.entry[i].score > 0xffff)
+ has_large_score_values = TRUE;
+
+ putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
+ putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
+
+ putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
+ SaveScore_VERS(file, &scores);
+
+ putFileChunkBE(file, "INFO", info_chunk_size);
+ SaveScore_INFO(file, &scores);
+
+ putFileChunkBE(file, "NAME", name_chunk_size);
+ SaveScore_NAME(file, &scores);
+
+ if (has_large_score_values)
+ {
+ putFileChunkBE(file, "SC4R", sc4r_chunk_size);
+ SaveScore_SC4R(file, &scores);
+ }
+ else
+ {
+ putFileChunkBE(file, "SCOR", scor_chunk_size);
+ SaveScore_SCOR(file, &scores);
+ }
+
+ putFileChunkBE(file, "TIME", time_chunk_size);
+ SaveScore_TIME(file, &scores);
+
+ putFileChunkBE(file, "TAPE", tape_chunk_size);
+ SaveScore_TAPE(file, &scores);
+
+ fclose(file);
+
+ SetFilePermissions(filename, PERMS_PRIVATE);
+}
+
+void SaveScore(int nr)
+{
+ char *filename = getScoreFilename(nr);
+ int i;
+
+ // used instead of "leveldir_current->subdir" (for network games)
+ InitScoreDirectory(levelset.identifier);
+
+ scores.file_version = FILE_VERSION_ACTUAL;
+ scores.game_version = GAME_VERSION_ACTUAL;
+
+ strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
+ scores.level_identifier[MAX_FILENAME_LEN] = '\0';
+ scores.level_nr = level_nr;
+
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
+ if (scores.entry[i].score == 0 &&
+ scores.entry[i].time == 0 &&
+ strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
+ break;
+
+ scores.num_entries = i;
+
+ if (scores.num_entries == 0)
+ return;
+
+ SaveScoreToFilename(filename);
+}
+
+static void LoadServerScoreFromCache(int nr)
+{
+ struct ScoreEntry score_entry;
+ struct
+ {
+ void *value;
+ boolean is_string;
+ int string_size;
+ }
+ score_mapping[] =
+ {
+ { &score_entry.score, FALSE, 0 },
+ { &score_entry.time, FALSE, 0 },
+ { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
+ { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
+ { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
+ { &score_entry.id, FALSE, 0 },
+ { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
+ { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
+ { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
+ { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
+
+ { NULL, FALSE, 0 }
+ };
+ char *filename = getScoreCacheFilename(nr);
+ SetupFileHash *score_hash = loadSetupFileHash(filename);
+ int i, j;
+
+ server_scores.num_entries = 0;
+
+ if (score_hash == NULL)
+ return;
+
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
+ {
+ score_entry = server_scores.entry[i];
+
+ for (j = 0; score_mapping[j].value != NULL; j++)
+ {
+ char token[10];
+
+ sprintf(token, "%02d.%d", i, j);
+
+ char *value = getHashEntry(score_hash, token);
+
+ if (value == NULL)
+ continue;
+
+ if (score_mapping[j].is_string)
+ {
+ char *score_value = (char *)score_mapping[j].value;
+ int value_size = score_mapping[j].string_size;
+
+ strncpy(score_value, value, value_size);
+ score_value[value_size] = '\0';
+ }
+ else
+ {
+ int *score_value = (int *)score_mapping[j].value;
+
+ *score_value = atoi(value);
+ }
+
+ server_scores.num_entries = i + 1;
+ }
+
+ server_scores.entry[i] = score_entry;
+ }
+
+ freeSetupFileHash(score_hash);
+}
+
+void LoadServerScore(int nr, boolean download_score)
+{
+ if (!setup.use_api_server)
+ return;
+
+ // always start with reliable default values
+ setServerScoreInfoToDefaults();
+
+ // 1st step: load server scores from cache file (which may not exist)
+ // (this should prevent reading it while the thread is writing to it)
+ LoadServerScoreFromCache(nr);
+
+ if (download_score && runtime.use_api_server)
+ {
+ // 2nd step: download server scores from score server to cache file
+ // (as thread, as it might time out if the server is not reachable)
+ ApiGetScoreAsThread(nr);
+ }
+}
+
+void PrepareScoreTapesForUpload(char *leveldir_subdir)
+{
+ MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
+
+ // if score tape not uploaded, ask for uploading missing tapes later
+ if (!setup.has_remaining_tapes)
+ setup.ask_for_remaining_tapes = TRUE;
+
+ setup.provide_uploading_tapes = TRUE;
+ setup.has_remaining_tapes = TRUE;
+
+ SaveSetup_ServerSetup();
+}
+
+void SaveServerScore(int nr, boolean tape_saved)
+{
+ if (!runtime.use_api_server)
+ {
+ PrepareScoreTapesForUpload(leveldir_current->subdir);
+
+ return;
+ }
+
+ ApiAddScoreAsThread(nr, tape_saved, NULL);
+}
+
+void SaveServerScoreFromFile(int nr, boolean tape_saved,
+ char *score_tape_filename)
+{
+ if (!runtime.use_api_server)
+ return;
+
+ ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
+}
+
+void LoadLocalAndServerScore(int nr, boolean download_score)
+{
+ int last_added_local = scores.last_added_local;
+ boolean force_last_added = scores.force_last_added;
+
+ // needed if only showing server scores
+ setScoreInfoToDefaults();
+
+ if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
+ LoadScore(nr);
+
+ // restore last added local score entry (before merging server scores)
+ scores.last_added = scores.last_added_local = last_added_local;
+
+ if (setup.use_api_server &&
+ !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
+ {
+ // load server scores from cache file and trigger update from server
+ LoadServerScore(nr, download_score);
+
+ // merge local scores with scores from server
+ MergeServerScore();
+ }
+
+ if (force_last_added)
+ scores.force_last_added = force_last_added;
+}
+
+
+// ============================================================================
+// setup file functions
+// ============================================================================
+
+#define TOKEN_STR_PLAYER_PREFIX "player_"
+
+
+static struct TokenInfo global_setup_tokens[] =
+{
+ {
+ TYPE_STRING,
+ &setup.player_name, "player_name"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.multiple_users, "multiple_users"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.sound, "sound"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.sound_loops, "repeating_sound_loops"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.sound_music, "background_music"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.sound_simple, "simple_sound_effects"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.toons, "toons"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.scroll_delay, "scroll_delay"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.forced_scroll_delay, "forced_scroll_delay"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.scroll_delay_value, "scroll_delay_value"
+ },
+ {
+ TYPE_STRING,
+ &setup.engine_snapshot_mode, "engine_snapshot_mode"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.engine_snapshot_memory, "engine_snapshot_memory"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.fade_screens, "fade_screens"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.autorecord, "automatic_tape_recording"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.autorecord_after_replay, "autorecord_after_replay"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.auto_pause_on_start, "auto_pause_on_start"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.show_titlescreen, "show_titlescreen"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.quick_doors, "quick_doors"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.team_mode, "team_mode"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.handicap, "handicap"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.skip_levels, "skip_levels"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.increment_levels, "increment_levels"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.auto_play_next_level, "auto_play_next_level"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.count_score_after_game, "count_score_after_game"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.show_scores_after_game, "show_scores_after_game"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.time_limit, "time_limit"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.fullscreen, "fullscreen"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.window_scaling_percent, "window_scaling_percent"
+ },
+ {
+ TYPE_STRING,
+ &setup.window_scaling_quality, "window_scaling_quality"
+ },
+ {
+ TYPE_STRING,
+ &setup.screen_rendering_mode, "screen_rendering_mode"
+ },
+ {
+ TYPE_STRING,
+ &setup.vsync_mode, "vsync_mode"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.ask_on_escape, "ask_on_escape"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.ask_on_escape_editor, "ask_on_escape_editor"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.ask_on_game_over, "ask_on_game_over"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.ask_on_quit_game, "ask_on_quit_game"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.ask_on_quit_program, "ask_on_quit_program"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.quick_switch, "quick_player_switch"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.input_on_focus, "input_on_focus"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.prefer_aga_graphics, "prefer_aga_graphics"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.game_speed_extended, "game_speed_extended"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.game_frame_delay, "game_frame_delay"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.sp_show_border_elements, "sp_show_border_elements"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.small_game_graphics, "small_game_graphics"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.show_load_save_buttons, "show_load_save_buttons"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
+ },
+ {
+ TYPE_STRING,
+ &setup.scores_in_highscore_list, "scores_in_highscore_list"
+ },
+ {
+ TYPE_STRING,
+ &setup.graphics_set, "graphics_set"
+ },
+ {
+ TYPE_STRING,
+ &setup.sounds_set, "sounds_set"
+ },
+ {
+ TYPE_STRING,
+ &setup.music_set, "music_set"
+ },
+ {
+ TYPE_SWITCH3,
+ &setup.override_level_graphics, "override_level_graphics"
+ },
+ {
+ TYPE_SWITCH3,
+ &setup.override_level_sounds, "override_level_sounds"
+ },
+ {
+ TYPE_SWITCH3,
+ &setup.override_level_music, "override_level_music"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.volume_simple, "volume_simple"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.volume_loops, "volume_loops"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.volume_music, "volume_music"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.network_mode, "network_mode"
+ },
+ {
+ TYPE_PLAYER,
+ &setup.network_player_nr, "network_player"
+ },
+ {
+ TYPE_STRING,
+ &setup.network_server_hostname, "network_server_hostname"
+ },
+ {
+ TYPE_STRING,
+ &setup.touch.control_type, "touch.control_type"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.move_distance, "touch.move_distance"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.drop_distance, "touch.drop_distance"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.transparency, "touch.transparency"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.draw_outlined, "touch.draw_outlined"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.draw_pressed, "touch.draw_pressed"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.touch.overlay_buttons, "touch.overlay_buttons"
+ },
+};
+
+static struct TokenInfo auto_setup_tokens[] =
+{
+ {
+ TYPE_INTEGER,
+ &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
+ },
+};
+
+static struct TokenInfo server_setup_tokens[] =
+{
+ {
+ TYPE_STRING,
+ &setup.player_uuid, "player_uuid"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.player_version, "player_version"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.use_api_server, TEST_PREFIX "use_api_server"
+ },
+ {
+ TYPE_STRING,
+ &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
+ },
+ {
+ TYPE_STRING,
+ &setup.api_server_password, TEST_PREFIX "api_server_password"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
+ },
+};
+
+static struct TokenInfo editor_setup_tokens[] =
+{
+ {
+ TYPE_SWITCH,
+ &setup.editor.el_classic, "editor.el_classic"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor.el_custom, "editor.el_custom"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor.el_user_defined, "editor.el_user_defined"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor.el_dynamic, "editor.el_dynamic"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor.el_headlines, "editor.el_headlines"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor.show_element_token, "editor.show_element_token"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
+ },
+};
+
+static struct TokenInfo editor_cascade_setup_tokens[] =
+{
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_em, "editor.cascade.el_em"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_df, "editor.cascade.el_df"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_es, "editor.cascade.el_es"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_user, "editor.cascade.el_user"
+ },
+ {
+ TYPE_SWITCH,
+ &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
+ },
+};
+
+static struct TokenInfo shortcut_setup_tokens[] =
+{
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.save_game, "shortcut.save_game"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.load_game, "shortcut.load_game"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.restart_game, "shortcut.restart_game"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.tape_eject, "shortcut.tape_eject"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.tape_extra, "shortcut.tape_extra"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.tape_stop, "shortcut.tape_stop"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.tape_pause, "shortcut.tape_pause"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.tape_record, "shortcut.tape_record"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.tape_play, "shortcut.tape_play"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.sound_simple, "shortcut.sound_simple"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.sound_loops, "shortcut.sound_loops"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.sound_music, "shortcut.sound_music"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.snap_left, "shortcut.snap_left"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.snap_right, "shortcut.snap_right"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.snap_up, "shortcut.snap_up"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.shortcut.snap_down, "shortcut.snap_down"
+ },
+};
+
+static struct SetupInputInfo setup_input;
+static struct TokenInfo player_setup_tokens[] =
+{
+ {
+ TYPE_BOOLEAN,
+ &setup_input.use_joystick, ".use_joystick"
+ },
+ {
+ TYPE_STRING,
+ &setup_input.joy.device_name, ".joy.device_name"
+ },
+ {
+ TYPE_INTEGER,
+ &setup_input.joy.xleft, ".joy.xleft"
+ },
+ {
+ TYPE_INTEGER,
+ &setup_input.joy.xmiddle, ".joy.xmiddle"
+ },
+ {
+ TYPE_INTEGER,
+ &setup_input.joy.xright, ".joy.xright"
+ },
+ {
+ TYPE_INTEGER,
+ &setup_input.joy.yupper, ".joy.yupper"
+ },
+ {
+ TYPE_INTEGER,
+ &setup_input.joy.ymiddle, ".joy.ymiddle"
+ },
+ {
+ TYPE_INTEGER,
+ &setup_input.joy.ylower, ".joy.ylower"
+ },
+ {
+ TYPE_INTEGER,
+ &setup_input.joy.snap, ".joy.snap_field"
+ },
+ {
+ TYPE_INTEGER,
+ &setup_input.joy.drop, ".joy.place_bomb"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup_input.key.left, ".key.move_left"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup_input.key.right, ".key.move_right"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup_input.key.up, ".key.move_up"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup_input.key.down, ".key.move_down"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup_input.key.snap, ".key.snap_field"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup_input.key.drop, ".key.place_bomb"
+ },
+};
+
+static struct TokenInfo system_setup_tokens[] =
+{
+ {
+ TYPE_STRING,
+ &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
+ },
+ {
+ TYPE_STRING,
+ &setup.system.sdl_videodriver, "system.sdl_videodriver"
+ },
+ {
+ TYPE_STRING,
+ &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.system.audio_fragment_size, "system.audio_fragment_size"
+ },
+};
+
+static struct TokenInfo internal_setup_tokens[] =
+{
+ {
+ TYPE_STRING,
+ &setup.internal.program_title, "program_title"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.program_version, "program_version"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.program_author, "program_author"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.program_email, "program_email"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.program_website, "program_website"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.program_copyright, "program_copyright"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.program_company, "program_company"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.program_icon_file, "program_icon_file"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.default_graphics_set, "default_graphics_set"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.default_sounds_set, "default_sounds_set"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.default_music_set, "default_music_set"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.fallback_graphics_file, "fallback_graphics_file"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.fallback_sounds_file, "fallback_sounds_file"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.fallback_music_file, "fallback_music_file"
+ },
+ {
+ TYPE_STRING,
+ &setup.internal.default_level_series, "default_level_series"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.internal.default_window_width, "default_window_width"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.internal.default_window_height, "default_window_height"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.show_scaling_in_title, "show_scaling_in_title"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.create_user_levelset, "create_user_levelset"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_screens_from_main, "info_screens_from_main"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_game, "menu_game"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_engines, "menu_engines"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_editor, "menu_editor"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_graphics, "menu_graphics"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_sound, "menu_sound"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_artwork, "menu_artwork"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_input, "menu_input"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_touch, "menu_touch"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_shortcuts, "menu_shortcuts"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_exit, "menu_exit"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_save_and_exit, "menu_save_and_exit"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_title, "info_title"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_elements, "info_elements"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_music, "info_music"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_credits, "info_credits"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_program, "info_program"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_version, "info_version"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_levelset, "info_levelset"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.internal.info_exit, "info_exit"
+ },
+};
+
+static struct TokenInfo debug_setup_tokens[] =
+{
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[0], "debug.frame_delay_0"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[1], "debug.frame_delay_1"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[2], "debug.frame_delay_2"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[3], "debug.frame_delay_3"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[4], "debug.frame_delay_4"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[5], "debug.frame_delay_5"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[6], "debug.frame_delay_6"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[7], "debug.frame_delay_7"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[8], "debug.frame_delay_8"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.frame_delay[9], "debug.frame_delay_9"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
+ },
+ {
+ TYPE_KEY_X11,
+ &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
+ {
+ TYPE_BOOLEAN,
+ &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
+ },
+ {
+ TYPE_SWITCH3,
+ &setup.debug.xsn_mode, "debug.xsn_mode"
+ },
+ {
+ TYPE_INTEGER,
+ &setup.debug.xsn_percent, "debug.xsn_percent"
+ },
+};
+
+static struct TokenInfo options_setup_tokens[] =
+{
+ {
+ TYPE_BOOLEAN,
+ &setup.options.verbose, "options.verbose"
+ },
+ {
+ TYPE_BOOLEAN,
+ &setup.options.debug, "options.debug"
+ },
+ {
+ TYPE_STRING,
+ &setup.options.debug_mode, "options.debug_mode"
+ },
+};
+
+static void setSetupInfoToDefaults(struct SetupInfo *si)
+{
+ int i;
+
+ si->player_name = getStringCopy(getDefaultUserName(user.nr));
+
+ si->multiple_users = TRUE;
+
+ si->sound = TRUE;
+ si->sound_loops = TRUE;
+ si->sound_music = TRUE;
+ si->sound_simple = TRUE;
+ si->toons = TRUE;
+ si->scroll_delay = TRUE;
+ si->forced_scroll_delay = FALSE;
+ si->scroll_delay_value = STD_SCROLL_DELAY;
+ si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
+ si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
+ si->fade_screens = TRUE;
+ si->autorecord = TRUE;
+ si->autorecord_after_replay = TRUE;
+ si->auto_pause_on_start = FALSE;
+ si->show_titlescreen = TRUE;
+ si->quick_doors = FALSE;
+ si->team_mode = FALSE;
+ si->handicap = TRUE;
+ si->skip_levels = TRUE;
+ si->increment_levels = TRUE;
+ si->auto_play_next_level = TRUE;
+ si->count_score_after_game = TRUE;
+ si->show_scores_after_game = TRUE;
+ si->time_limit = TRUE;
+ si->fullscreen = FALSE;
+ si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
+ si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
+ si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
+ si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
+ si->ask_on_escape = TRUE;
+ si->ask_on_escape_editor = TRUE;
+ si->ask_on_game_over = TRUE;
+ si->ask_on_quit_game = TRUE;
+ si->ask_on_quit_program = TRUE;
+ si->quick_switch = FALSE;
+ si->input_on_focus = FALSE;
+ si->prefer_aga_graphics = TRUE;
+ si->prefer_lowpass_sounds = FALSE;
+ si->prefer_extra_panel_items = TRUE;
+ si->game_speed_extended = FALSE;
+ si->game_frame_delay = GAME_FRAME_DELAY;
+ si->sp_show_border_elements = FALSE;
+ si->small_game_graphics = FALSE;
+ si->show_load_save_buttons = FALSE;
+ si->show_undo_redo_buttons = FALSE;
+ si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
+
+ si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
+ si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
+ si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
+
+ si->override_level_graphics = FALSE;
+ si->override_level_sounds = FALSE;
+ si->override_level_music = FALSE;
+
+ si->volume_simple = 100; // percent
+ si->volume_loops = 100; // percent
+ si->volume_music = 100; // percent
+
+ si->network_mode = FALSE;
+ si->network_player_nr = 0; // first player
+ si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
+
+ si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
+ si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
si->touch.draw_outlined = TRUE;
si->touch.grid_initialized = video.initialized;
+ si->touch.overlay_buttons = FALSE;
+
si->editor.el_boulderdash = TRUE;
si->editor.el_emerald_mine = TRUE;
si->editor.el_emerald_mine_club = TRUE;
si->editor.show_element_token = FALSE;
+ si->editor.show_read_only_warning = TRUE;
+
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.restart_game = DEFAULT_KEY_RESTART_GAME;
+ si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
}
+ si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
si->internal.choose_from_top_leveldir = FALSE;
si->internal.show_scaling_in_title = TRUE;
+ si->internal.create_user_levelset = TRUE;
+ si->internal.info_screens_from_main = FALSE;
si->internal.default_window_width = WIN_XSIZE_DEFAULT;
si->internal.default_window_height = WIN_YSIZE_DEFAULT;
si->debug.show_frames_per_second = FALSE;
+ si->debug.xsn_mode = AUTO;
+ si->debug.xsn_percent = 0;
+
si->options.verbose = FALSE;
+ si->options.debug = FALSE;
+ si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
#if defined(PLATFORM_ANDROID)
si->fullscreen = TRUE;
+ si->touch.overlay_buttons = TRUE;
#endif
+
+ setHideSetupEntry(&setup.debug.xsn_mode);
}
static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
}
+static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
+{
+ si->player_uuid = NULL; // (will be set later)
+ si->player_version = 1; // (will be set later)
+
+ si->use_api_server = TRUE;
+ si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
+ si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
+ si->ask_for_uploading_tapes = TRUE;
+ si->ask_for_remaining_tapes = FALSE;
+ si->provide_uploading_tapes = TRUE;
+ si->ask_for_using_api_server = TRUE;
+ si->has_remaining_tapes = FALSE;
+}
+
static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
{
si->editor_cascade.el_bd = TRUE;
si->editor_cascade.el_steel_chars = FALSE;
si->editor_cascade.el_ce = FALSE;
si->editor_cascade.el_ge = FALSE;
+ si->editor_cascade.el_es = FALSE;
si->editor_cascade.el_ref = FALSE;
si->editor_cascade.el_user = FALSE;
si->editor_cascade.el_dynamic = FALSE;
{
char *hide_setup_token = getHideSetupToken(setup_value);
+ if (hide_setup_hash == NULL)
+ hide_setup_hash = newSetupFileHash();
+
if (setup_value != NULL)
setHashEntry(hide_setup_hash, hide_setup_token, "");
}
-static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
+void removeHideSetupEntry(void *setup_value)
{
- // !!! 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);
- setHideSetupEntry(setup_value);
+ if (setup_value != NULL)
+ removeHashEntry(hide_setup_hash, hide_setup_token);
}
boolean hideSetupEntry(void *setup_value)
// check if this setup option should be hidden in the setup menu
if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
- setHideSetupEntryRaw(token_text, token_info[token_nr].value);
+ setHideSetupEntry(token_info[token_nr].value);
+
+ free(token_hide_text);
}
static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
token_info[token_nr].text);
}
-static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
+static void decodeSetupFileHash_Default(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 < ARRAY_SIZE(global_setup_tokens); i++)
setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
- setup = si;
- // virtual buttons setup
setup.touch.grid_initialized = TRUE;
for (i = 0; i < 2; i++)
{
}
}
- // editor setup
- sei = setup.editor;
for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
- setup.editor = sei;
- // shortcut setup
- ssi = setup.shortcut;
for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
- setup.shortcut = ssi;
- // player setup
for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
{
char prefix[30];
sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
- sii = setup.input[pnr];
+ setup_input = setup.input[pnr];
for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
{
char full_token[100];
setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
full_token);
}
- setup.input[pnr] = sii;
+ setup.input[pnr] = setup_input;
}
- // system setup
- syi = setup.system;
for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
- setup.system = syi;
- // internal setup
- sxi = setup.internal;
for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
- setup.internal = sxi;
- // debug setup
- sdi = setup.debug;
for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
- setup.debug = sdi;
- // options setup
- soi = setup.options;
for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
- setup.options = soi;
setHideRelatedSetupEntries();
}
if (!setup_file_hash)
return;
- // auto setup
- sasi = setup.auto_setup;
for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
setSetupInfo(auto_setup_tokens, i,
getHashEntry(setup_file_hash,
auto_setup_tokens[i].text));
- setup.auto_setup = sasi;
+}
+
+static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
+{
+ int i;
+
+ if (!setup_file_hash)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
+ setSetupInfo(server_setup_tokens, i,
+ getHashEntry(setup_file_hash,
+ server_setup_tokens[i].text));
}
static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
if (!setup_file_hash)
return;
- // editor cascade setup
- seci = setup.editor_cascade;
for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
setSetupInfo(editor_cascade_setup_tokens, i,
getHashEntry(setup_file_hash,
editor_cascade_setup_tokens[i].text));
- setup.editor_cascade = seci;
+}
+
+void LoadUserNames(void)
+{
+ int last_user_nr = user.nr;
+ int i;
+
+ if (global.user_names != NULL)
+ {
+ for (i = 0; i < MAX_PLAYER_NAMES; i++)
+ checked_free(global.user_names[i]);
+
+ checked_free(global.user_names);
+ }
+
+ global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
+
+ for (i = 0; i < MAX_PLAYER_NAMES; i++)
+ {
+ user.nr = i;
+
+ SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
+
+ if (setup_file_hash)
+ {
+ char *player_name = getHashEntry(setup_file_hash, "player_name");
+
+ global.user_names[i] = getFixedUserName(player_name);
+
+ freeSetupFileHash(setup_file_hash);
+ }
+
+ if (global.user_names[i] == NULL)
+ global.user_names[i] = getStringCopy(getDefaultUserName(i));
+ }
+
+ user.nr = last_user_nr;
}
void LoadSetupFromFilename(char *filename)
if (setup_file_hash)
{
- decodeSetupFileHash(setup_file_hash);
+ decodeSetupFileHash_Default(setup_file_hash);
freeSetupFileHash(setup_file_hash);
}
else
{
- Error(ERR_DEBUG, "using default setup values");
+ Debug("setup", "using default setup values");
}
}
char *player_name_new;
// needed to work around problems with fixed length strings
- player_name_new = get_corrected_login_name(setup.player_name);
+ player_name_new = getFixedUserName(setup.player_name);
free(setup.player_name);
setup.player_name = player_name_new;
MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
}
-void LoadSetup(void)
+void LoadSetup_Default(void)
{
char *filename;
// try to load setup values from default setup file
filename = getDefaultSetupFilename();
+ if (fileExists(filename))
+ LoadSetupFromFilename(filename);
+
+ // try to load setup values from platform setup file
+ filename = getPlatformSetupFilename();
+
if (fileExists(filename))
LoadSetupFromFilename(filename);
free(filename);
}
+void LoadSetup_ServerSetup(void)
+{
+ char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
+ SetupFileHash *setup_file_hash = NULL;
+
+ // always start with reliable default values
+ setSetupInfoToDefaults_ServerSetup(&setup);
+
+ setup_file_hash = loadSetupFileHash(filename);
+
+ if (setup_file_hash)
+ {
+ decodeSetupFileHash_ServerSetup(setup_file_hash);
+
+ freeSetupFileHash(setup_file_hash);
+ }
+
+ free(filename);
+
+ if (setup.player_uuid == NULL)
+ {
+ // player UUID does not yet exist in setup file
+ setup.player_uuid = getStringCopy(getUUID());
+ setup.player_version = 2;
+
+ SaveSetup_ServerSetup();
+ }
+}
+
void LoadSetup_EditorCascade(void)
{
char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
freeSetupFileHash(setup_file_hash);
}
- free(filename);
+ free(filename);
+}
+
+void LoadSetup(void)
+{
+ LoadSetup_Default();
+ LoadSetup_AutoSetup();
+ LoadSetup_ServerSetup();
+ LoadSetup_EditorCascade();
}
static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
if (!(file = fopen(filename, MODE_READ)))
{
- Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
+ Warn("cannot read game controller mappings file '%s'", filename);
return;
}
fclose(file);
}
-void SaveSetup(void)
+void SaveSetup_Default(void)
{
char *filename = getSetupFilename();
FILE *file;
if (!(file = fopen(filename, MODE_WRITE)))
{
- Error(ERR_WARN, "cannot write setup file '%s'", filename);
+ Warn("cannot write setup file '%s'", filename);
+
return;
}
fprintFileHeader(file, SETUP_FILENAME);
- // global setup
- si = setup;
for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
{
// just to make things nicer :)
- if (global_setup_tokens[i].value == &si.sound ||
- global_setup_tokens[i].value == &si.graphics_set ||
- global_setup_tokens[i].value == &si.volume_simple ||
- global_setup_tokens[i].value == &si.network_mode ||
- global_setup_tokens[i].value == &si.touch.control_type ||
- global_setup_tokens[i].value == &si.touch.grid_xsize[0] ||
- global_setup_tokens[i].value == &si.touch.grid_xsize[1])
+ if (global_setup_tokens[i].value == &setup.multiple_users ||
+ global_setup_tokens[i].value == &setup.sound ||
+ global_setup_tokens[i].value == &setup.graphics_set ||
+ global_setup_tokens[i].value == &setup.volume_simple ||
+ global_setup_tokens[i].value == &setup.network_mode ||
+ global_setup_tokens[i].value == &setup.touch.control_type ||
+ global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
+ global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
fprintf(file, "\n");
fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
}
- // virtual buttons setup
for (i = 0; i < 2; i++)
{
int grid_xsize = setup.touch.grid_xsize[i];
}
}
- // editor setup
- sei = setup.editor;
fprintf(file, "\n");
for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
- // shortcut setup
- ssi = setup.shortcut;
fprintf(file, "\n");
for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
- // player setup
for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
{
char prefix[30];
sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
fprintf(file, "\n");
- sii = setup.input[pnr];
+ setup_input = setup.input[pnr];
for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
}
- // system setup
- syi = setup.system;
fprintf(file, "\n");
for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
- // internal setup
// (internal setup values not saved to user setup file)
- // debug setup
- sdi = setup.debug;
fprintf(file, "\n");
for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
- fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
+ if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
+ setup.debug.xsn_mode != AUTO)
+ fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
- // options setup
- soi = setup.options;
fprintf(file, "\n");
for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
if (!(file = fopen(filename, MODE_WRITE)))
{
- Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
+ Warn("cannot write auto setup file '%s'", filename);
+
free(filename);
+
return;
}
fprintFileHeader(file, AUTOSETUP_FILENAME);
- sasi = setup.auto_setup;
for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
free(filename);
}
+void SaveSetup_ServerSetup(void)
+{
+ char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
+ FILE *file;
+ int i;
+
+ InitUserDataDirectory();
+
+ if (!(file = fopen(filename, MODE_WRITE)))
+ {
+ Warn("cannot write server setup file '%s'", filename);
+
+ free(filename);
+
+ return;
+ }
+
+ fprintFileHeader(file, SERVERSETUP_FILENAME);
+
+ for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
+ {
+ // just to make things nicer :)
+ if (server_setup_tokens[i].value == &setup.use_api_server)
+ fprintf(file, "\n");
+
+ fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
+ }
+
+ fclose(file);
+
+ SetFilePermissions(filename, PERMS_PRIVATE);
+
+ free(filename);
+}
+
void SaveSetup_EditorCascade(void)
{
char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
if (!(file = fopen(filename, MODE_WRITE)))
{
- Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
+ Warn("cannot write editor cascade state file '%s'", filename);
+
free(filename);
+
return;
}
fprintFileHeader(file, EDITORCASCADE_FILENAME);
- seci = setup.editor_cascade;
for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
free(filename);
}
+void SaveSetup(void)
+{
+ SaveSetup_Default();
+ SaveSetup_AutoSetup();
+ SaveSetup_ServerSetup();
+ SaveSetup_EditorCascade();
+}
+
static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
char *filename)
{
if (!(file = fopen(filename, MODE_WRITE)))
{
- Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
+ 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(void)
+{
+ char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
+ SetupFileHash *setup_file_hash;
+ int i;
+
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ {
+ if (element_info[i].custom_description != NULL)
+ {
+ free(element_info[i].custom_description);
+ element_info[i].custom_description = NULL;
+ }
+ }
+
+ if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
+ return;
+
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ {
+ char *token = getStringCat2(element_info[i].token_name, ".name");
+ char *value = getHashEntry(setup_file_hash, token);
+
+ if (value != NULL)
+ element_info[i].custom_description = getStringCopy(value);
+
+ free(token);
+ }
+
+ freeSetupFileHash(setup_file_hash);
+}
+
+static int getElementFromToken(char *token)
+{
+ char *value = getHashEntry(element_token_hash, token);
+
+ if (value != NULL)
+ return atoi(value);
+
+ Warn("unknown element token '%s'", token);
+
+ return EL_UNDEFINED;
+}
+
+void FreeGlobalAnimEventInfo(void)
+{
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+
+ if (gaei->event_list == NULL)
+ return;
+
+ int i;
+
+ for (i = 0; i < gaei->num_event_lists; i++)
+ {
+ checked_free(gaei->event_list[i]->event_value);
+ checked_free(gaei->event_list[i]);
+ }
+
+ checked_free(gaei->event_list);
+
+ gaei->event_list = NULL;
+ gaei->num_event_lists = 0;
+}
+
+static int AddGlobalAnimEventList(void)
+{
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+ int list_pos = gaei->num_event_lists++;
+
+ gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
+ sizeof(struct GlobalAnimEventListInfo *));
+
+ gaei->event_list[list_pos] =
+ checked_calloc(sizeof(struct GlobalAnimEventListInfo));
+
+ struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
+
+ gaeli->event_value = NULL;
+ gaeli->num_event_values = 0;
+
+ return list_pos;
+}
+
+static int AddGlobalAnimEventValue(int list_pos, int event_value)
+{
+ // do not add empty global animation events
+ if (event_value == ANIM_EVENT_NONE)
+ return list_pos;
+
+ // if list position is undefined, create new list
+ if (list_pos == ANIM_EVENT_UNDEFINED)
+ list_pos = AddGlobalAnimEventList();
+
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+ struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
+ int value_pos = gaeli->num_event_values++;
+
+ gaeli->event_value = checked_realloc(gaeli->event_value,
+ gaeli->num_event_values * sizeof(int *));
+
+ gaeli->event_value[value_pos] = event_value;
+
+ return list_pos;
+}
+
+int GetGlobalAnimEventValue(int list_pos, int value_pos)
+{
+ if (list_pos == ANIM_EVENT_UNDEFINED)
+ return ANIM_EVENT_NONE;
+
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+ struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
+
+ return gaeli->event_value[value_pos];
+}
+
+int GetGlobalAnimEventValueCount(int list_pos)
+{
+ if (list_pos == ANIM_EVENT_UNDEFINED)
+ return 0;
+
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+ struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
+
+ return gaeli->num_event_values;
+}
+
+// This function checks if a string <s> of the format "string1, string2, ..."
+// exactly contains a string <s_contained>.
+
+static boolean string_has_parameter(char *s, char *s_contained)
+{
+ char *substring;
+
+ if (s == NULL || s_contained == NULL)
+ return FALSE;
+
+ if (strlen(s_contained) > strlen(s))
+ return FALSE;
+
+ if (strncmp(s, s_contained, strlen(s_contained)) == 0)
+ {
+ char next_char = s[strlen(s_contained)];
+
+ // check if next character is delimiter or whitespace
+ if (next_char == ',' || next_char == '\0' ||
+ next_char == ' ' || next_char == '\t')
+ return TRUE;
+ }
+
+ // check if string contains another parameter string after a comma
+ substring = strchr(s, ',');
+ if (substring == NULL) // string does not contain a comma
+ return FALSE;
+
+ // advance string pointer to next character after the comma
+ substring++;
+
+ // skip potential whitespaces after the comma
+ while (*substring == ' ' || *substring == '\t')
+ substring++;
+
+ return string_has_parameter(substring, s_contained);
+}
+
+static int get_anim_parameter_value_ce(char *s)
+{
+ char *s_ptr = s;
+ char *pattern_1 = "ce_change:custom_";
+ char *pattern_2 = ".page_";
+ int pattern_1_len = strlen(pattern_1);
+ char *matching_char = strstr(s_ptr, pattern_1);
+ int result = ANIM_EVENT_NONE;
+
+ if (matching_char == NULL)
+ return ANIM_EVENT_NONE;
+
+ result = ANIM_EVENT_CE_CHANGE;
+
+ s_ptr = matching_char + pattern_1_len;
+
+ // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ {
+ int gic_ce_nr = (*s_ptr++ - '0');
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ {
+ gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
+ }
+
+ if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
+ return ANIM_EVENT_NONE;
+
+ // custom element stored as 0 to 255
+ gic_ce_nr--;
+
+ result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
+ }
+ else
+ {
+ // invalid custom element number specified
+
+ return ANIM_EVENT_NONE;
+ }
+
+ // check for change page number ("page_X" or "page_XX") (optional)
+ if (strPrefix(s_ptr, pattern_2))
+ {
+ s_ptr += strlen(pattern_2);
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ {
+ int gic_page_nr = (*s_ptr++ - '0');
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
+
+ if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
+ return ANIM_EVENT_NONE;
+
+ // change page stored as 1 to 32 (0 means "all change pages")
+
+ result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
+ }
+ else
+ {
+ // invalid animation part number specified
+
+ return ANIM_EVENT_NONE;
+ }
+ }
+
+ // discard result if next character is neither delimiter nor whitespace
+ if (!(*s_ptr == ',' || *s_ptr == '\0' ||
+ *s_ptr == ' ' || *s_ptr == '\t'))
+ return ANIM_EVENT_NONE;
+
+ return result;
+}
+
+static int get_anim_parameter_value(char *s)
+{
+ int event_value[] =
+ {
+ ANIM_EVENT_CLICK,
+ ANIM_EVENT_INIT,
+ ANIM_EVENT_START,
+ ANIM_EVENT_END,
+ ANIM_EVENT_POST
+ };
+ char *pattern_1[] =
+ {
+ "click:anim_",
+ "init:anim_",
+ "start:anim_",
+ "end:anim_",
+ "post:anim_"
+ };
+ char *pattern_2 = ".part_";
+ char *matching_char = NULL;
+ char *s_ptr = s;
+ int pattern_1_len = 0;
+ int result = ANIM_EVENT_NONE;
+ int i;
+
+ result = get_anim_parameter_value_ce(s);
+
+ if (result != ANIM_EVENT_NONE)
+ return result;
+
+ for (i = 0; i < ARRAY_SIZE(event_value); i++)
+ {
+ matching_char = strstr(s_ptr, pattern_1[i]);
+ pattern_1_len = strlen(pattern_1[i]);
+ result = event_value[i];
+
+ if (matching_char != NULL)
+ break;
+ }
+
+ if (matching_char == NULL)
+ return ANIM_EVENT_NONE;
+
+ s_ptr = matching_char + pattern_1_len;
+
+ // check for main animation number ("anim_X" or "anim_XX")
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ {
+ int gic_anim_nr = (*s_ptr++ - '0');
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
+
+ if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
+ return ANIM_EVENT_NONE;
+
+ result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
+ }
+ else
+ {
+ // invalid main animation number specified
+
+ return ANIM_EVENT_NONE;
+ }
+
+ // check for animation part number ("part_X" or "part_XX") (optional)
+ if (strPrefix(s_ptr, pattern_2))
+ {
+ s_ptr += strlen(pattern_2);
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ {
+ int gic_part_nr = (*s_ptr++ - '0');
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
+
+ if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
+ return ANIM_EVENT_NONE;
+
+ result |= gic_part_nr << ANIM_EVENT_PART_BIT;
+ }
+ else
+ {
+ // invalid animation part number specified
+
+ return ANIM_EVENT_NONE;
+ }
+ }
+
+ // discard result if next character is neither delimiter nor whitespace
+ if (!(*s_ptr == ',' || *s_ptr == '\0' ||
+ *s_ptr == ' ' || *s_ptr == '\t'))
+ return ANIM_EVENT_NONE;
+
+ return result;
+}
+
+static int get_anim_parameter_values(char *s)
+{
+ int list_pos = ANIM_EVENT_UNDEFINED;
+ int event_value = ANIM_EVENT_DEFAULT;
+
+ if (string_has_parameter(s, "any"))
+ event_value |= ANIM_EVENT_ANY;
+
+ if (string_has_parameter(s, "click:self") ||
+ string_has_parameter(s, "click") ||
+ string_has_parameter(s, "self"))
+ event_value |= ANIM_EVENT_SELF;
+
+ if (string_has_parameter(s, "unclick:any"))
+ event_value |= ANIM_EVENT_UNCLICK_ANY;
+
+ // if animation event found, add it to global animation event list
+ if (event_value != ANIM_EVENT_NONE)
+ list_pos = AddGlobalAnimEventValue(list_pos, event_value);
+
+ while (s != NULL)
+ {
+ // add optional "click:anim_X" or "click:anim_X.part_X" parameter
+ event_value = get_anim_parameter_value(s);
- return;
- }
+ // if animation event found, add it to global animation event list
+ if (event_value != ANIM_EVENT_NONE)
+ list_pos = AddGlobalAnimEventValue(list_pos, event_value);
- BEGIN_HASH_ITERATION(mappings_hash, itr)
- {
- fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
+ // continue with next part of the string, starting with next comma
+ s = strchr(s + 1, ',');
}
- END_HASH_ITERATION(mappings_hash, itr)
- fclose(file);
+ return list_pos;
}
-void SaveSetup_AddGameControllerMapping(char *mapping)
+static int get_anim_action_parameter_value(char *token)
{
- char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
- SetupFileHash *mappings_hash = newSetupFileHash();
+ // check most common default case first to massively speed things up
+ if (strEqual(token, ARG_UNDEFINED))
+ return ANIM_EVENT_ACTION_NONE;
- InitUserDataDirectory();
+ int result = getImageIDFromToken(token);
- // load existing personal game controller mappings
- LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
+ if (result == -1)
+ {
+ char *gfx_token = getStringCat2("gfx.", token);
- // add new mapping to personal game controller mappings
- addGameControllerMappingToHash(mappings_hash, mapping);
+ result = getImageIDFromToken(gfx_token);
- // save updated personal game controller mappings
- SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
+ checked_free(gfx_token);
+ }
- freeSetupFileHash(mappings_hash);
- free(filename);
-}
+ if (result == -1)
+ {
+ Key key = getKeyFromX11KeyName(token);
-void LoadCustomElementDescriptions(void)
-{
- char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
- SetupFileHash *setup_file_hash;
- int i;
+ if (key != KSYM_UNDEFINED)
+ result = -(int)key;
+ }
- for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ if (result == -1)
{
- if (element_info[i].custom_description != NULL)
+ if (isURL(token))
{
- free(element_info[i].custom_description);
- element_info[i].custom_description = NULL;
+ result = get_hash_from_key(token); // unsigned int => int
+ result = ABS(result); // may be negative now
+ result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
+
+ setHashEntry(anim_url_hash, int2str(result, 0), token);
}
}
- if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
- return;
+ if (result == -1)
+ result = ANIM_EVENT_ACTION_NONE;
- for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ return result;
+}
+
+int get_parameter_value(char *value_raw, char *suffix, int type)
+{
+ char *value = getStringToLower(value_raw);
+ int result = 0; // probably a save default value
+
+ if (strEqual(suffix, ".direction"))
{
- char *token = getStringCat2(element_info[i].token_name, ".name");
- char *value = getHashEntry(setup_file_hash, token);
+ result = (strEqual(value, "left") ? MV_LEFT :
+ strEqual(value, "right") ? MV_RIGHT :
+ strEqual(value, "up") ? MV_UP :
+ strEqual(value, "down") ? MV_DOWN : MV_NONE);
+ }
+ else if (strEqual(suffix, ".position"))
+ {
+ result = (strEqual(value, "left") ? POS_LEFT :
+ strEqual(value, "right") ? POS_RIGHT :
+ strEqual(value, "top") ? POS_TOP :
+ strEqual(value, "upper") ? POS_UPPER :
+ strEqual(value, "middle") ? POS_MIDDLE :
+ strEqual(value, "lower") ? POS_LOWER :
+ strEqual(value, "bottom") ? POS_BOTTOM :
+ strEqual(value, "any") ? POS_ANY :
+ strEqual(value, "ce") ? POS_CE :
+ strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
+ }
+ else if (strEqual(suffix, ".align"))
+ {
+ result = (strEqual(value, "left") ? ALIGN_LEFT :
+ strEqual(value, "right") ? ALIGN_RIGHT :
+ strEqual(value, "center") ? ALIGN_CENTER :
+ strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
+ }
+ else if (strEqual(suffix, ".valign"))
+ {
+ result = (strEqual(value, "top") ? VALIGN_TOP :
+ strEqual(value, "bottom") ? VALIGN_BOTTOM :
+ strEqual(value, "middle") ? VALIGN_MIDDLE :
+ strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
+ }
+ else if (strEqual(suffix, ".anim_mode"))
+ {
+ result = (string_has_parameter(value, "none") ? ANIM_NONE :
+ string_has_parameter(value, "loop") ? ANIM_LOOP :
+ string_has_parameter(value, "linear") ? ANIM_LINEAR :
+ string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
+ string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
+ string_has_parameter(value, "random") ? ANIM_RANDOM :
+ string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
+ string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
+ string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
+ string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
+ string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
+ string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
+ string_has_parameter(value, "centered") ? ANIM_CENTERED :
+ string_has_parameter(value, "all") ? ANIM_ALL :
+ string_has_parameter(value, "tiled") ? ANIM_TILED :
+ ANIM_DEFAULT);
- if (value != NULL)
- element_info[i].custom_description = getStringCopy(value);
+ if (string_has_parameter(value, "once"))
+ result |= ANIM_ONCE;
- free(token);
+ if (string_has_parameter(value, "reverse"))
+ result |= ANIM_REVERSE;
+
+ if (string_has_parameter(value, "opaque_player"))
+ result |= ANIM_OPAQUE_PLAYER;
+
+ if (string_has_parameter(value, "static_panel"))
+ result |= ANIM_STATIC_PANEL;
}
+ else if (strEqual(suffix, ".init_event") ||
+ strEqual(suffix, ".anim_event"))
+ {
+ result = get_anim_parameter_values(value);
+ }
+ else if (strEqual(suffix, ".init_delay_action") ||
+ strEqual(suffix, ".anim_delay_action") ||
+ strEqual(suffix, ".post_delay_action") ||
+ strEqual(suffix, ".init_event_action") ||
+ strEqual(suffix, ".anim_event_action"))
+ {
+ result = get_anim_action_parameter_value(value_raw);
+ }
+ else if (strEqual(suffix, ".class"))
+ {
+ result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
+ get_hash_from_key(value));
+ }
+ else if (strEqual(suffix, ".style"))
+ {
+ result = STYLE_DEFAULT;
- freeSetupFileHash(setup_file_hash);
-}
+ if (string_has_parameter(value, "accurate_borders"))
+ result |= STYLE_ACCURATE_BORDERS;
-static int getElementFromToken(char *token)
-{
- char *value = getHashEntry(element_token_hash, token);
+ if (string_has_parameter(value, "inner_corners"))
+ result |= STYLE_INNER_CORNERS;
- if (value != NULL)
- return atoi(value);
+ if (string_has_parameter(value, "reverse"))
+ result |= STYLE_REVERSE;
- Error(ERR_WARN, "unknown element token '%s'", token);
+ if (string_has_parameter(value, "leftmost_position"))
+ result |= STYLE_LEFTMOST_POSITION;
- return EL_UNDEFINED;
+ if (string_has_parameter(value, "block_clicks"))
+ result |= STYLE_BLOCK;
+
+ if (string_has_parameter(value, "passthrough_clicks"))
+ result |= STYLE_PASSTHROUGH;
+
+ if (string_has_parameter(value, "multiple_actions"))
+ result |= STYLE_MULTIPLE_ACTIONS;
+
+ if (string_has_parameter(value, "consume_ce_event"))
+ result |= STYLE_CONSUME_CE_EVENT;
+ }
+ else if (strEqual(suffix, ".fade_mode"))
+ {
+ result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
+ string_has_parameter(value, "fade") ? FADE_MODE_FADE :
+ string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
+ string_has_parameter(value, "melt") ? FADE_MODE_MELT :
+ string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
+ FADE_MODE_DEFAULT);
+ }
+ else if (strEqual(suffix, ".auto_delay_unit"))
+ {
+ result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
+ string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
+ AUTO_DELAY_UNIT_DEFAULT);
+ }
+ else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
+ {
+ result = gfx.get_font_from_token_function(value);
+ }
+ else // generic parameter of type integer or boolean
+ {
+ result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
+ type == TYPE_INTEGER ? get_integer_from_string(value) :
+ type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
+ ARG_UNDEFINED_VALUE);
+ }
+
+ free(value);
+
+ return result;
}
static int get_token_parameter_value(char *token, char *value_raw)
return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
}
-void InitMenuDesignSettings_Static(void)
+void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
+ boolean ignore_defaults)
{
int i;
- // always start with reliable default values from static default config
for (i = 0; image_config_vars[i].token != NULL; i++)
{
- char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
+ char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
+
+ // (ignore definitions set to "[DEFAULT]" which are already initialized)
+ if (ignore_defaults && strEqual(value, ARG_DEFAULT))
+ continue;
if (value != NULL)
*image_config_vars[i].value =
}
}
+void InitMenuDesignSettings_Static(void)
+{
+ // always start with reliable default values from static default config
+ InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
+}
+
static void InitMenuDesignSettings_SpecialPreProcessing(void)
{
int i;
title_initial_first_default.post_delay;
titlescreen_initial_first_default.auto_delay =
title_initial_first_default.auto_delay;
+ titlescreen_initial_first_default.auto_delay_unit =
+ title_initial_first_default.auto_delay_unit;
titlescreen_first_default.fade_mode = title_first_default.fade_mode;
titlescreen_first_default.fade_delay = title_first_default.fade_delay;
titlescreen_first_default.post_delay = title_first_default.post_delay;
titlescreen_first_default.auto_delay = title_first_default.auto_delay;
+ titlescreen_first_default.auto_delay_unit =
+ title_first_default.auto_delay_unit;
titlemessage_initial_first_default.fade_mode =
title_initial_first_default.fade_mode;
titlemessage_initial_first_default.fade_delay =
title_initial_first_default.post_delay;
titlemessage_initial_first_default.auto_delay =
title_initial_first_default.auto_delay;
+ titlemessage_initial_first_default.auto_delay_unit =
+ title_initial_first_default.auto_delay_unit;
titlemessage_first_default.fade_mode = title_first_default.fade_mode;
titlemessage_first_default.fade_delay = title_first_default.fade_delay;
titlemessage_first_default.post_delay = title_first_default.post_delay;
titlemessage_first_default.auto_delay = title_first_default.auto_delay;
+ titlemessage_first_default.auto_delay_unit =
+ title_first_default.auto_delay_unit;
titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
titlescreen_initial_default.post_delay = title_initial_default.post_delay;
titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
+ titlescreen_initial_default.auto_delay_unit =
+ title_initial_default.auto_delay_unit;
titlescreen_default.fade_mode = title_default.fade_mode;
titlescreen_default.fade_delay = title_default.fade_delay;
titlescreen_default.post_delay = title_default.post_delay;
titlescreen_default.auto_delay = title_default.auto_delay;
+ titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
titlemessage_initial_default.post_delay = title_initial_default.post_delay;
- titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
+ titlemessage_initial_default.auto_delay_unit =
+ title_initial_default.auto_delay_unit;
titlemessage_default.fade_mode = title_default.fade_mode;
titlemessage_default.fade_delay = title_default.fade_delay;
titlemessage_default.post_delay = title_default.post_delay;
titlemessage_default.auto_delay = title_default.auto_delay;
+ titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
// special case: initialize "ARG_DEFAULT" values in static default config
// (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
if (vp_playfield->max_height != -1)
- vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
+ vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
// adjust playfield position according to specified alignment
}
}
+static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
+ boolean initialize)
+{
+ // special case: check if network and preview player positions are redefined,
+ // to compare this later against the main menu level preview being redefined
+ struct TokenIntPtrInfo menu_config_players[] =
+ {
+ { "main.network_players.x", &menu.main.network_players.redefined },
+ { "main.network_players.y", &menu.main.network_players.redefined },
+ { "main.preview_players.x", &menu.main.preview_players.redefined },
+ { "main.preview_players.y", &menu.main.preview_players.redefined },
+ { "preview.x", &preview.redefined },
+ { "preview.y", &preview.redefined }
+ };
+ int i;
+
+ if (initialize)
+ {
+ for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
+ *menu_config_players[i].value = FALSE;
+ }
+ else
+ {
+ for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
+ if (getHashEntry(hash, menu_config_players[i].token) != NULL)
+ *menu_config_players[i].value = TRUE;
+ }
+}
+
+static void InitMenuDesignSettings_PreviewPlayers(void)
+{
+ InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
+}
+
+static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
+{
+ InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
+}
+
static void LoadMenuDesignSettingsFromFilename(char *filename)
{
static struct TitleFadingInfo tfi;
{ TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
{ TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
{ TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
+ { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
{ -1, NULL, NULL }
};
{ TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
{ TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
{ TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
+ { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
{ -1, NULL, NULL }
};
{
{ "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
{ "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
- { "menu.list_size.INFO", &menu.list_size_info[i] }
+ { "menu.list_size.INFO", &menu.list_size_info[i] },
+ { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
+ { "menu.tile_size.INFO", &menu.tile_size_info[i] }
};
for (j = 0; j < ARRAY_SIZE(menu_config); j++)
struct TokenIntPtrInfo menu_config[] =
{
{ "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
+ { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
{ "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
{ "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
{ "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
}
}
- // special case: check if network and preview player positions are redefined,
- // to compare this later against the main menu level preview being redefined
- struct TokenIntPtrInfo menu_config_players[] =
- {
- { "main.network_players.x", &menu.main.network_players.redefined },
- { "main.network_players.y", &menu.main.network_players.redefined },
- { "main.preview_players.x", &menu.main.preview_players.redefined },
- { "main.preview_players.y", &menu.main.preview_players.redefined },
- { "preview.x", &preview.redefined },
- { "preview.y", &preview.redefined }
- };
-
- for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
- *menu_config_players[i].value = FALSE;
-
- for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
- if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
- *menu_config_players[i].value = TRUE;
-
// read (and overwrite with) values that may be specified in config file
- for (i = 0; image_config_vars[i].token != NULL; i++)
- {
- char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
+ InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
- // (ignore definitions set to "[DEFAULT]" which are already initialized)
- if (value != NULL && !strEqual(value, ARG_DEFAULT))
- *image_config_vars[i].value =
- get_token_parameter_value(image_config_vars[i].token, value);
- }
+ // special case: check if network and preview player positions are redefined
+ InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
freeSetupFileHash(setup_file_hash);
}
InitMenuDesignSettings_Static();
InitMenuDesignSettings_SpecialPreProcessing();
+ InitMenuDesignSettings_PreviewPlayers();
if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
{
{
if (num_unknown_tokens == 0)
{
- Error(ERR_INFO_LINE, "-");
- Error(ERR_INFO, "warning: unknown token(s) found in config file:");
- Error(ERR_INFO, "- config file: '%s'", filename);
+ Warn("---");
+ Warn("unknown token(s) found in config file:");
+ Warn("- config file: '%s'", filename);
num_unknown_tokens++;
}
- Error(ERR_INFO, "- token: '%s'", list->token);
+ Warn("- token: '%s'", list->token);
}
}
if (num_unknown_tokens > 0)
- Error(ERR_INFO_LINE, "-");
+ Warn("---");
while (*num_elements % 4) // pad with empty elements, if needed
(*elements)[(*num_elements)++] = EL_EMPTY;
#if 0
for (i = 0; i < *num_elements; i++)
- printf("editor: element '%s' [%d]\n",
- element_info[(*elements)[i]].token_name, (*elements)[i]);
+ Debug("editor", "element '%s' [%d]\n",
+ element_info[(*elements)[i]].token_name, (*elements)[i]);
#endif
}
{ "artist_header", &tmp_music_file_info.artist_header },
{ "album_header", &tmp_music_file_info.album_header },
{ "year_header", &tmp_music_file_info.year_header },
+ { "played_header", &tmp_music_file_info.played_header },
{ "title", &tmp_music_file_info.title },
{ "artist", &tmp_music_file_info.artist },
{ "album", &tmp_music_file_info.album },
{ "year", &tmp_music_file_info.year },
+ { "played", &tmp_music_file_info.played },
{ NULL, NULL },
};
void LoadMusicInfo(void)
{
- char *music_directory = getCustomMusicDirectory();
+ int num_music_noconf = getMusicListSize_NoConf();
int num_music = getMusicListSize();
- int num_music_noconf = 0;
int num_sounds = getSoundListSize();
- Directory *dir;
- DirectoryEntry *dir_entry;
struct FileInfo *music, *sound;
struct MusicFileInfo *next, **new;
+
int i;
while (music_file_info != NULL)
checked_free(music_file_info->artist_header);
checked_free(music_file_info->album_header);
checked_free(music_file_info->year_header);
+ checked_free(music_file_info->played_header);
checked_free(music_file_info->title);
checked_free(music_file_info->artist);
checked_free(music_file_info->album);
checked_free(music_file_info->year);
+ checked_free(music_file_info->played);
free(music_file_info);
new = &music_file_info;
- for (i = 0; i < num_music; i++)
+ // get (configured or unconfigured) music file info for all levels
+ for (i = leveldir_current->first_level;
+ i <= leveldir_current->last_level; i++)
{
- music = getMusicListEntry(i);
+ int music_nr;
- if (music->filename == NULL)
- continue;
+ if (levelset.music[i] != MUS_UNDEFINED)
+ {
+ // get music file info for configured level music
+ music_nr = levelset.music[i];
+ }
+ else if (num_music_noconf > 0)
+ {
+ // get music file info for unconfigured level music
+ int level_pos = i - leveldir_current->first_level;
- if (strEqual(music->filename, UNDEFINED_FILENAME))
+ music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
+ }
+ else
+ {
continue;
+ }
- // a configured file may be not recognized as music
- if (!FileIsMusic(music->filename))
+ char *basename = getMusicInfoEntryFilename(music_nr);
+
+ if (basename == NULL)
continue;
- if (!music_info_listed(music_file_info, music->filename))
+ if (!music_info_listed(music_file_info, basename))
{
- *new = get_music_file_info(music->filename, i);
+ *new = get_music_file_info(basename, music_nr);
if (*new != NULL)
new = &(*new)->next;
}
}
- if ((dir = openDirectory(music_directory)) == NULL)
- {
- Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
- return;
- }
-
- while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
+ // get music file info for all remaining configured music files
+ for (i = 0; i < num_music; i++)
{
- char *basename = dir_entry->basename;
- boolean music_already_used = FALSE;
- int i;
-
- // skip all music files that are configured in music config file
- for (i = 0; i < num_music; i++)
- {
- music = getMusicListEntry(i);
-
- if (music->filename == NULL)
- continue;
+ music = getMusicListEntry(i);
- if (strEqual(basename, music->filename))
- {
- music_already_used = TRUE;
- break;
- }
- }
+ if (music->filename == NULL)
+ continue;
- if (music_already_used)
+ if (strEqual(music->filename, UNDEFINED_FILENAME))
continue;
- if (!FileIsMusic(dir_entry->filename))
+ // a configured file may be not recognized as music
+ if (!FileIsMusic(music->filename))
continue;
- if (!music_info_listed(music_file_info, basename))
+ if (!music_info_listed(music_file_info, music->filename))
{
- *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
+ *new = get_music_file_info(music->filename, i);
if (*new != NULL)
new = &(*new)->next;
}
-
- num_music_noconf++;
}
- closeDirectory(dir);
-
+ // get sound file info for all configured sound files
for (i = 0; i < num_sounds; i++)
{
sound = getSoundListEntry(i);
new = &(*new)->next;
}
}
+
+ // add pointers to previous list nodes
+
+ struct MusicFileInfo *node = music_file_info;
+
+ while (node != NULL)
+ {
+ if (node->next)
+ node->next->prev = node;
+
+ node = node->next;
+ }
}
static void add_helpanim_entry(int element, int action, int direction,
{
if (token_nr == 0)
{
- Error(ERR_INFO_LINE, "-");
- Error(ERR_INFO, "warning: unknown token(s) found in config file:");
- Error(ERR_INFO, "- config file: '%s'", filename);
+ Warn("---");
+ Warn("unknown token(s) found in config file:");
+ Warn("- config file: '%s'", filename);
}
- Error(ERR_INFO, "- token: '%s'", token);
+ Warn("- token: '%s'", token);
}
static void print_unknown_token_end(int token_nr)
{
if (token_nr > 0)
- Error(ERR_INFO_LINE, "-");
+ Warn("---");
}
void LoadHelpAnimInfo(void)
#if 0
for (i = 0; i < num_list_entries; i++)
- printf("::: '%s': %d, %d, %d => %d\n",
- EL_NAME(helpanim_info[i].element),
- helpanim_info[i].element,
- helpanim_info[i].action,
- helpanim_info[i].direction,
- helpanim_info[i].delay);
+ Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
+ EL_NAME(helpanim_info[i].element),
+ helpanim_info[i].element,
+ helpanim_info[i].action,
+ helpanim_info[i].direction,
+ helpanim_info[i].delay);
#endif
}
#if 0
BEGIN_HASH_ITERATION(helptext_info, itr)
{
- printf("::: '%s' => '%s'\n",
- HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
+ Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
+ HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
}
END_HASH_ITERATION(hash, itr)
#endif
global.convert_leveldir);
if (convert_leveldir == NULL)
- Error(ERR_EXIT, "no such level identifier: '%s'",
- global.convert_leveldir);
+ Fail("no such level identifier: '%s'", global.convert_leveldir);
leveldir_current = convert_leveldir;
Print("converting level ... ");
+#if 0
+ // special case: conversion of some EMC levels as requested by ACME
+ level.game_engine_type = GAME_ENGINE_TYPE_RND;
+#endif
+
level_filename = getDefaultLevelFilename(level_nr);
new_level = !fileExists(level_filename);
for (i = 0; i < NUM_FILE_ELEMENTS; i++)
{
- Bitmap *src_bitmap;
- int src_x, src_y;
int element = getMappedElement(i);
- int graphic = el2edimg(element);
char basename1[16];
char basename2[16];
char *filename1;
char *filename2;
- sprintf(basename1, "%03d.bmp", i);
- sprintf(basename2, "%03ds.bmp", i);
+ sprintf(basename1, "%04d.bmp", i);
+ sprintf(basename2, "%04ds.bmp", i);
- filename1 = getPath2(global.create_images_dir, basename1);
- filename2 = getPath2(global.create_images_dir, basename2);
+ filename1 = getPath2(global.create_sketch_images_dir, basename1);
+ filename2 = getPath2(global.create_sketch_images_dir, basename2);
- getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
- BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
- 0, 0);
+ DrawSizedElement(0, 0, element, TILESIZE);
+ BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
- Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
+ Fail("cannot save level sketch image file '%s'", filename1);
- getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
- BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
+ DrawSizedElement(0, 0, element, MINI_TILESIZE);
+ BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
- Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
+ Fail("cannot save level sketch image file '%s'", filename2);
free(filename1);
free(filename2);
+ // create corresponding SQL statements (for normal and small images)
+ if (i < 1000)
+ {
+ printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
+ printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
+ }
+
+ printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
+ printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
+
+ // optional: create content for forum level sketch demonstration post
if (options.debug)
- printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
+ fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
}
FreeBitmap(bitmap1);
FreeBitmap(bitmap2);
if (options.debug)
- printf("\n");
+ fprintf(stderr, "\n");
+
+ Info("%d normal and small images created", NUM_FILE_ELEMENTS);
+
+ CloseAllAndExit(0);
+}
+
+
+// ----------------------------------------------------------------------------
+// create and save images for element collecting animations (raw BMP format)
+// ----------------------------------------------------------------------------
+
+static boolean createCollectImage(int element)
+{
+ return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
+}
+
+void CreateCollectElementImages(void)
+{
+ int i, j;
+ int num_steps = 8;
+ int anim_frames = num_steps - 1;
+ int tile_size = TILESIZE;
+ int anim_width = tile_size * anim_frames;
+ int anim_height = tile_size;
+ int num_collect_images = 0;
+ int pos_collect_images = 0;
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (createCollectImage(i))
+ num_collect_images++;
+
+ Info("Creating %d element collecting animation images ...",
+ num_collect_images);
+
+ int dst_width = anim_width * 2;
+ int dst_height = anim_height * num_collect_images / 2;
+ Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
+ char *basename_bmp = "RocksCollect.bmp";
+ char *basename_png = "RocksCollect.png";
+ char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
+ char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
+ int len_filename_bmp = strlen(filename_bmp);
+ int len_filename_png = strlen(filename_png);
+ int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
+ char cmd_convert[max_command_len];
+
+ snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
+ filename_bmp,
+ filename_png);
+
+ // force using RGBA surface for destination bitmap
+ SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
+ SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
+
+ dst_bitmap->surface =
+ SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (!createCollectImage(i))
+ continue;
+
+ int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
+ int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
+ int graphic = el2img(i);
+ char *token_name = element_info[i].token_name;
+ Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ Info("- creating collecting image for '%s' ...", token_name);
+
+ getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+
+ BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
+ tile_size, tile_size, 0, 0);
+
+ // force using RGBA surface for temporary bitmap (using transparent black)
+ SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
+ SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
+
+ tmp_bitmap->surface =
+ SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
+
+ tmp_bitmap->surface_masked = tmp_bitmap->surface;
+
+ for (j = 0; j < anim_frames; j++)
+ {
+ int frame_size_final = tile_size * (anim_frames - j) / num_steps;
+ int frame_size = frame_size_final * num_steps;
+ int offset = (tile_size - frame_size_final) / 2;
+ Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
+
+ while (frame_size > frame_size_final)
+ {
+ frame_size /= 2;
+
+ Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
+
+ FreeBitmap(frame_bitmap);
+
+ frame_bitmap = half_bitmap;
+ }
+
+ BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
+ frame_size_final, frame_size_final,
+ dst_x + j * tile_size + offset, dst_y + offset);
+
+ FreeBitmap(frame_bitmap);
+ }
+
+ tmp_bitmap->surface_masked = NULL;
+
+ FreeBitmap(tmp_bitmap);
+
+ pos_collect_images++;
+ }
+
+ if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
+ Fail("cannot save element collecting image file '%s'", filename_bmp);
+
+ FreeBitmap(dst_bitmap);
+
+ Info("Converting image file from BMP to PNG ...");
+
+ if (system(cmd_convert) != 0)
+ Fail("converting image file failed");
+
+ unlink(filename_bmp);
- Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
+ Info("Done.");
CloseAllAndExit(0);
}
int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
int i;
- SDLInitVideoDisplay();
+ InitVideoDefaults();
ReCreateBitmap(&backbuffer, video.width, video.height);
}
if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
- Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
+ Fail("cannot save CE graphics file '%s'", dst_filename);
FreeBitmap(bitmap);