<commit_before>/*
    Sylverant Ship Server
    Copyright (C) 2012, 2013, 2014 <NAME>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License version 3
    as published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <sylverant/debug.h>
#include <sylverant/prs.h>

#include "mapdata.h"
#include "lobby.h"
#include "clients.h"

/* Enemy battle parameters. The array is organized in the following levels:
   multi/single player, episode, difficulty, entry.*/
static bb_battle_param_t battle_params[2][3][4][0x60];

/* Player levelup data */
bb_level_table_t char_stats;

/* Parsed enemy data. Organized similarly to the battle parameters, except that
   the last level is the actual areas themselves (and there's no difficulty
   level in there). */
static parsed_map_t bb_parsed_maps[2][3][0x10];
static parsed_objs_t bb_parsed_objs[2][3][0x10];

/* V2 Parsed enemy data. This is much simpler, since there's no episodes nor
   single-player mode to worry about. */
static parsed_map_t v2_parsed_maps[0x10];
static parsed_objs_t v2_parsed_objs[0x10];

/* Did we read in v2 map data? */
static int have_v2_maps = 0;

/* GC Parsed enemy data. Midway point between v2 and BB. Two episodes, but no
   single player mode. */
static parsed_map_t gc_parsed_maps[2][0x10];
static parsed_objs_t gc_parsed_objs[2][0x10];

/* Did we read in gc map data? */
static int have_gc_maps = 0;

/* Header for sections of the .dat files for quests. */
typedef struct quest_dat_hdr {
    uint32_t obj_type;
    uint32_t next_hdr;
    uint32_t area;
    uint32_t size;
    uint8_t data[];
} quest_dat_hdr_t;

static int read_param_file(bb_battle_param_t dst[4][0x60], const char *fn) {
    FILE *fp;
    const size_t sz = 0x60 * sizeof(bb_battle_param_t);

    if(!(fp = fopen(fn, "rb"))) {
        debug(DBG_ERROR, "Cannot open %s for reading: %s\n", fn,
              strerror(errno));
        return 1;
    }

    /* Read each difficulty in... */
    if(fread(dst[0], 1, sz, fp) != sz) {
        debug(DBG_ERROR, "Cannot read data from %s: %s\n", fn, strerror(errno));
        return 1;
    }

    if(fread(dst[1], 1, sz, fp) != sz) {
        debug(DBG_ERROR, "Cannot read data from %s: %s\n", fn, strerror(errno));
        return 1;
    }

    if(fread(dst[2], 1, sz, fp) != sz) {
        debug(DBG_ERROR, "Cannot read data from %s: %s\n", fn, strerror(errno));
        return 1;
    }

    if(fread(dst[3], 1, sz, fp) != sz) {
        debug(DBG_ERROR, "Cannot read data from %s: %s\n", fn, strerror(errno));
        return 1;
    }

    fclose(fp);
    return 0;
}

static int read_level_data(const char *fn) {
    uint8_t *buf;
    int decsize;

#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
    int i, j;
#endif

    /* Read in the file and decompress it. */
    if((decsize = prs_decompress_file(fn, &buf)) < 0) {
        debug(DBG_ERROR, "Cannot read levels %s: %s\n", fn, strerror(-decsize));
        return -1;
    }

    memcpy(&char_stats, buf, sizeof(bb_level_table_t));

#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
    /* Swap all the exp values */
    for(j = 0; j < 12; ++j) {
        for(i = 0; i < 200; ++i) {
            char_stats[j][i].exp = LE32(char_stats[j][i].exp);
        }
    }
#endif

    /* Clean up... */
    free(buf);

    return 0;
}

static const uint32_t maps[3][0x20] = {
    {1,1,1,5,1,5,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1,1,1,1,1,1,1,1,1,1},
    {1,1,2,1,2,1,2,1,2,1,1,3,1,3,1,3,2,2,1,3,2,2,2,2,1,1,1,1,1,1,1,1},
    {1,1,1,3,1,3,1,3,1,3,1,3,3,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

static const uint32_t sp_maps[3][0x20] = {
    {1,1,1,3,1,3,3,1,3,1,3,1,3,2,3,2,3,2,3,2,3,2,1,1,1,1,1,1,1,1,1,1},
    {1,1,2,1,2,1,2,1,2,1,1,3,1,3,1,3,2,2,1,3,2,1,2,1,1,1,1,1,1,1,1,1},
    {1,1,1,3,1,3,1,3,1,3,1,3,3,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

static const int max_area[3] = { 0x0E, 0x0F, 0x09 };

static int parse_map(map_enemy_t *en, int en_ct, game_enemies_t *game,
                     int ep, int alt) {
    int i, j;
    game_enemy_t *gen;
    void *tmp;
    uint32_t count = 0;
    uint16_t n_clones;
    int acc;

    /* Allocate the space first */
    if(!(gen = (game_enemy_t *)malloc(sizeof(game_enemy_t) * 0xB50))) {
        debug(DBG_ERROR, "Cannot allocate enemies: %s\n", strerror(errno));
        return -1;
    }

    /* Clear it */
    memset(gen, 0, sizeof(game_enemy_t) * 0xB50);

    /* Parse each enemy. */
    for(i = 0; i < en_ct; ++i) {
        n_clones = en[i].num_clones;

        switch(en[i].base) {
            case 0x0040:    /* Hildebear & Hildetorr */
                acc = en[i].skin & 0x01;
                gen[count].bp_entry = 0x49 + acc;
                gen[count].rt_index = 0x01 + acc;
                break;

            case 0x0041:    /* Rappies */
                acc = en[i].skin & 0x01;
                if(ep == 3) {   /* Del Rappy & Sand Rappy */
                    if(alt) {
                        gen[count].bp_entry = 0x17 + acc;
                        gen[count].rt_index = 0x11 + acc;
                    }
                    else {
                        gen[count].bp_entry = 0x05 + acc;
                        gen[count].rt_index = 0x11 + acc;
                    }
                }
                else {
                    if(acc) {
                        gen[count].bp_entry = 0x19;

                        if(ep == 1) {
                            gen[count].rt_index = 0x06;
                        }
                        else {
                            /* We need to fill this in when we make the lobby,
                               since it's dependent on the event. */
                            gen[count].rt_index = (uint8_t)-1;
                        }
                    }
                    else {
                        gen[count].bp_entry = 0x18;
                        gen[count].rt_index = 0x05;
                    }
                }
                break;

            case 0x0042:    /* Monest + 30 Mothmants */
                gen[count].bp_entry = 0x01;
                gen[count].rt_index = 0x04;

                for(j = 0; j < 30; ++j) {
                    ++count;
                    gen[count].bp_entry = 0x00;
                    gen[count].rt_index = 0x03;
                }
                break;

            case 0x0043:    /* Savage Wolf & Barbarous Wolf */
                acc = (en[i].reserved[10] & 0x800000) ? 1 : 0;
                gen[count].bp_entry = 0x02 + acc;
                gen[count].rt_index = 0x07 + acc;
                break;

            case 0x0044:    /* Booma family */
                acc = en[i].skin % 3;
                gen[count].bp_entry = 0x4B + acc;
                gen[count].rt_index = 0x09 + acc;
                break;

            case 0x0060:    /* Grass Assassin */
                gen[count].bp_entry = 0x4E;
                gen[count].rt_index = 0x0C;
                break;

            case 0x0061:    /* Del Lily, Poison Lily, Nar Lily */
                if(ep == 2 && alt) {
                    gen[count].bp_entry = 0x25;
                    gen[count].rt_index = 0x53;
                }
                else {
                    acc = (en[i].reserved[10] & 0x800000) ? 1 : 0;
                    gen[count].bp_entry = 0x04 + acc;
                    gen[count].rt_index = 0x0D + acc;
                }
                break;

            case 0x0062:    /* Nano Dragon */
                gen[count].bp_entry = 0x1A;
                gen[count].rt_index = 0x0E;
                break;

            case 0x0063:    /* Shark Family */
                acc = en[i].skin % 3;
                gen[count].bp_entry = 0x4F + acc;
                gen[count].rt_index = 0x10 + acc;
                break;

            case 0x0064:    /* Slime + 4 clones */
                acc = (en[i].reserved[10] & 0x800000) ? 1 : 0;
                gen[count].bp_entry = 0x30 - acc;
                gen[count].rt_index = 0x13 + acc;<commit_msg>Fix Nano Dragon's RT/PT index.<commit_after>/*
    Sylverant Ship Server
    Copyright (C) 2012, 2013, 2014 <NAME>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License version 3
    as published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <sylverant/debug.h>
#include <sylverant/prs.h>

#include "mapdata.h"
#include "lobby.h"
#include "clients.h"

/* Enemy battle parameters. The array is organized in the following levels:
   multi/single player, episode, difficulty, entry.*/
static bb_battle_param_t battle_params[2][3][4][0x60];

/* Player levelup data */
bb_level_table_t char_stats;

/* Parsed enemy data. Organized similarly to the battle parameters, except that
   the last level is the actual areas themselves (and there's no difficulty
   level in there). */
static parsed_map_t bb_parsed_maps[2][3][0x10];
static parsed_objs_t bb_parsed_objs[2][3][0x10];

/* V2 Parsed enemy data. This is much simpler, since there's no episodes nor
   single-player mode to worry about. */
static parsed_map_t v2_parsed_maps[0x10];
static parsed_objs_t v2_parsed_objs[0x10];

/* Did we read in v2 map data? */
static int have_v2_maps = 0;

/* GC Parsed enemy data. Midway point between v2 and BB. Two episodes, but no
   single player mode. */
static parsed_map_t gc_parsed_maps[2][0x10];
static parsed_objs_t gc_parsed_objs[2][0x10];

/* Did we read in gc map data? */
static int have_gc_maps = 0;

/* Header for sections of the .dat files for quests. */
typedef struct quest_dat_hdr {
    uint32_t obj_type;
    uint32_t next_hdr;
    uint32_t area;
    uint32_t size;
    uint8_t data[];
} quest_dat_hdr_t;

static int read_param_file(bb_battle_param_t dst[4][0x60], const char *fn) {
    FILE *fp;
    const size_t sz = 0x60 * sizeof(bb_battle_param_t);

    if(!(fp = fopen(fn, "rb"))) {
        debug(DBG_ERROR, "Cannot open %s for reading: %s\n", fn,
              strerror(errno));
        return 1;
    }

    /* Read each difficulty in... */
    if(fread(dst[0], 1, sz, fp) != sz) {
        debug(DBG_ERROR, "Cannot read data from %s: %s\n", fn, strerror(errno));
        return 1;
    }

    if(fread(dst[1], 1, sz, fp) != sz) {
        debug(DBG_ERROR, "Cannot read data from %s: %s\n", fn, strerror(errno));
        return 1;
    }

    if(fread(dst[2], 1, sz, fp) != sz) {
        debug(DBG_ERROR, "Cannot read data from %s: %s\n", fn, strerror(errno));
        return 1;
    }

    if(fread(dst[3], 1, sz, fp) != sz) {
        debug(DBG_ERROR, "Cannot read data from %s: %s\n", fn, strerror(errno));
        return 1;
    }

    fclose(fp);
    return 0;
}

static int read_level_data(const char *fn) {
    uint8_t *buf;
    int decsize;

#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
    int i, j;
#endif

    /* Read in the file and decompress it. */
    if((decsize = prs_decompress_file(fn, &buf)) < 0) {
        debug(DBG_ERROR, "Cannot read levels %s: %s\n", fn, strerror(-decsize));
        return -1;
    }

    memcpy(&char_stats, buf, sizeof(bb_level_table_t));

#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
    /* Swap all the exp values */
    for(j = 0; j < 12; ++j) {
        for(i = 0; i < 200; ++i) {
            char_stats[j][i].exp = LE32(char_stats[j][i].exp);
        }
    }
#endif

    /* Clean up... */
    free(buf);

    return 0;
}

static const uint32_t maps[3][0x20] = {
    {1,1,1,5,1,5,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1,1,1,1,1,1,1,1,1,1},
    {1,1,2,1,2,1,2,1,2,1,1,3,1,3,1,3,2,2,1,3,2,2,2,2,1,1,1,1,1,1,1,1},
    {1,1,1,3,1,3,1,3,1,3,1,3,3,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

static const uint32_t sp_maps[3][0x20] = {
    {1,1,1,3,1,3,3,1,3,1,3,1,3,2,3,2,3,2,3,2,3,2,1,1,1,1,1,1,1,1,1,1},
    {1,1,2,1,2,1,2,1,2,1,1,3,1,3,1,3,2,2,1,3,2,1,2,1,1,1,1,1,1,1,1,1},
    {1,1,1,3,1,3,1,3,1,3,1,3,3,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

static const int max_area[3] = { 0x0E, 0x0F, 0x09 };

static int parse_map(map_enemy_t *en, int en_ct, game_enemies_t *game,
                     int ep, int alt) {
    int i, j;
    game_enemy_t *gen;
    void *tmp;
    uint32_t count = 0;
    uint16_t n_clones;
    int acc;

    /* Allocate the space first */
    if(!(gen = (game_enemy_t *)malloc(sizeof(game_enemy_t) * 0xB50))) {
        debug(DBG_ERROR, "Cannot allocate enemies: %s\n", strerror(errno));
        return -1;
    }

    /* Clear it */
    memset(gen, 0, sizeof(game_enemy_t) * 0xB50);

    /* Parse each enemy. */
    for(i = 0; i < en_ct; ++i) {
        n_clones = en[i].num_clones;

        switch(en[i].base) {
            case 0x0040:    /* Hildebear & Hildetorr */
                acc = en[i].skin & 0x01;
                gen[count].bp_entry = 0x49 + acc;
                gen[count].rt_index = 0x01 + acc;
                break;

            case 0x0041:    /* Rappies */
                acc = en[i].skin & 0x01;
                if(ep == 3) {   /* Del Rappy & Sand Rappy */
                    if(alt) {
                        gen[count].bp_entry = 0x17 + acc;
                        gen[count].rt_index = 0x11 + acc;
                    }
                    else {
                        gen[count].bp_entry = 0x05 + acc;
                        gen[count].rt_index = 0x11 + acc;
                    }
                }
                else {
                    if(acc) {
                        gen[count].bp_entry = 0x19;

                        if(ep == 1) {
                            gen[count].rt_index = 0x06;
                        }
                        else {
                            /* We need to fill this in when we make the lobby,
                               since it's dependent on the event. */
                            gen[count].rt_index = (uint8_t)-1;
                        }
                    }
                    else {
                        gen[count].bp_entry = 0x18;
                        gen[count].rt_index = 0x05;
                    }
                }
                break;

            case 0x0042:    /* Monest + 30 Mothmants */
                gen[count].bp_entry = 0x01;
                gen[count].rt_index = 0x04;

                for(j = 0; j < 30; ++j) {
                    ++count;
                    gen[count].bp_entry = 0x00;
                    gen[count].rt_index = 0x03;
                }
                break;

            case 0x0043:    /* Savage Wolf & Barbarous Wolf */
                acc = (en[i].reserved[10] & 0x800000) ? 1 : 0;
                gen[count].bp_entry = 0x02 + acc;
                gen[count].rt_index = 0x07 + acc;
                break;

            case 0x0044:    /* Booma family */
                acc = en[i].skin % 3;
                gen[count].bp_entry = 0x4B + acc;
                gen[count].rt_index = 0x09 + acc;
                break;

            case 0x0060:    /* Grass Assassin */
                gen[count].bp_entry = 0x4E;
                gen[count].rt_index = 0x0C;
                break;

            case 0x0061:    /* <NAME>, <NAME>, <NAME> */
                if(ep == 2 && alt) {
                    gen[count].bp_entry = 0x25;
                    gen[count].rt_index = 0x53;
                }
                else {
                    acc = (en[i].reserved[10] & 0x800000) ? 1 : 0;
                    gen[count].bp_entry = 0x04 + acc;
                    gen[count].rt_index = 0x0D + acc;
                }
                break;

            case 0x0062:    /* Nano Dragon */
                gen[count].bp_entry = 0x1A;
                gen[count].rt_index = 0x0F;
                break;

            case 0x0063:    /* Shark Family */
                acc = en[i].skin % 3;
                gen[count].bp_entry = 0x4F + acc;
                gen[count].rt_index = 0x10 + acc;
                break;

            case 0x0064:    /* Slime + 4 clones */
                acc = (en[i].reserved[10] & 0x800000) ? 1 : 0;
                gen[count].bp_entry = 0x30 - acc;
                gen[count].rt_index = 0x13 + acc;
{'fraction_non_alphanumeric': 0.1356657290201594, 'fraction_numerical': 0.05918893577121425, 'mean_word_length': 1.747987117552335, 'pattern_counts': {'":': 0, '<': 36, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6133091', 'n_tokens_mistral': 6668, 'n_tokens_neox': 6058, 'n_words': 1450}