3
0
mirror of https://github.com/CrazyRedMachine/popnhax.git synced 2025-02-03 12:53:35 +01:00
popnhax/popnhax/loader.cc
2023-03-05 00:15:06 +01:00

1287 lines
54 KiB
C++

#include <cstdio>
#include <cstring>
#include <io.h>
#include <windows.h>
#include "imports/avs.h"
#include "util/patch.h"
#include "xmlhelper.h"
#include "tableinfo.h"
#include "loader.h"
#include <algorithm>
#include <map>
#include <vector>
#include "SearchFile.h"
#define RET_COMPARE_INDEX(ptr, val, idx) \
if (strcmp((char *)ptr, val) == 0) { \
return idx; \
}
struct property *load_prop_file(const char *filename);
bool file_exists(const char *filename);
uint8_t *find_string(uint8_t *input, size_t len);
uint8_t *add_string(uint8_t *input);
void add_chart_type_flag(uint32_t cur_idx, int8_t flag);
uint32_t get_lapis_shape_id(uint8_t *lapis_shape_ptr);
uint32_t get_lapis_color_id(uint8_t *lapis_color_ptr);
uint16_t get_chara_idx(const uint8_t *chara_id);
fontstyle_entry *get_fontstyle(int32_t cur_idx);
character_entry *get_chara(int32_t cur_idx);
music_entry *get_music(int32_t cur_idx);
chart_entry *get_chart(int32_t cur_idx);
uint32_t add_style(int32_t cur_idx, uint32_t fontface, uint32_t color, uint32_t height,
uint32_t width);
uint32_t add_flavor(int32_t cur_idx, uint8_t *phrase1, uint8_t *phrase2, uint8_t *phrase3,
uint8_t *phrase4, uint8_t *phrase5, uint8_t *phrase6, uint8_t *birthday,
uint8_t chara1_birth_month, uint8_t chara2_birth_month,
uint8_t chara3_birth_month, uint8_t chara1_birth_date,
uint8_t chara2_birth_date, uint8_t chara3_birth_date, uint16_t style1,
bool style2_flag, uint16_t style3, uint32_t fontstyle_fontface,
uint32_t fontstyle_color, uint32_t fontstyle_height, uint32_t fontstyle_width);
uint32_t add_chara(int32_t cur_idx, uint8_t *chara_id_ptr, uint32_t flags, uint8_t *folder_ptr,
uint8_t *gg_ptr, uint8_t *cs_ptr, uint8_t *icon1_ptr, uint8_t *icon2_ptr,
uint16_t chara_xw, uint16_t chara_yh, uint32_t display_flags, int16_t flavor_idx,
uint8_t chara_variation_num, uint8_t *sort_name_ptr, uint8_t *disp_name_ptr,
uint32_t file_type, uint32_t lapis_shape, uint8_t lapis_color, uint8_t *ha_ptr,
uint8_t *catchtext_ptr, int16_t win2_trigger, uint32_t game_version);
uint32_t add_music(int32_t cur_idx, uint8_t *fw_genre_ptr, uint8_t *fw_title_ptr,
uint8_t *fw_artist_ptr, uint8_t *genre_ptr, uint8_t *title_ptr,
uint8_t *artist_ptr, uint16_t chara1, uint16_t chara2, uint32_t mask,
uint32_t folder, uint32_t cs_version, uint32_t categories, uint8_t *diffs,
uint16_t *charts, uint8_t *ha_ptr, uint32_t chara_x, uint32_t chara_y,
uint16_t *unk1, uint16_t *display_bpm, uint8_t *hold_flags, bool allow_resize);
uint32_t add_chart(uint32_t cur_idx, uint8_t *folder, uint8_t *filename, int32_t audio_param1,
int32_t audio_param2, int32_t audio_param3, int32_t audio_param4,
uint32_t file_type, uint16_t used_keys, bool override_idx);
void parse_charadb(const char *input_filename, const char *target);
void parse_musicdb(const char *input_filename, const char *target);
std::map<uint32_t, int8_t> chart_type_overrides;
uint32_t flavor_limit = 0;
uint8_t *filler_str = add_string((uint8_t *)"\x81\x5d\x00");
const uint32_t string_table_growth_size = 0x100000;
uint32_t string_table_size = 0;
uint32_t string_table_idx = 0;
uint8_t *string_table = NULL;
const uint32_t fontstyle_table_growth_size = 50;
uint32_t fontstyle_table_size = 0;
int32_t fontmax_style_id = -1;
uint8_t *fontstyle_table = NULL;
const uint32_t flavor_table_growth_size = 50;
uint32_t flavor_table_size = 0;
int32_t max_flavor_id = -1;
uint8_t *flavor_table = NULL;
std::map<uint32_t, bool> used_chara_id;
const uint32_t chara_table_growth_size = 50;
uint32_t chara_table_size = 0;
int32_t max_chara_id = -1;
uint8_t *chara_table = NULL;
static uint8_t chara_table_nullptr[1] = {};
std::map<uint32_t, bool> used_music_id;
const uint32_t music_table_growth_size = 50;
uint32_t music_table_size = 0;
int32_t max_music_id = -1;
uint8_t *music_table = NULL;
const uint32_t chart_table_growth_size = 50;
uint32_t chart_table_size = 0;
int32_t max_chart_id = -1;
uint8_t *chart_table = NULL;
bool file_exists(const char *filename) {
FILE *test = fopen(filename, "rb");
if (!test) {
return false;
}
fclose(test);
return true;
}
uint8_t *find_string(uint8_t *input, size_t len) {
for (uint32_t i = 0; i < string_table_size - 1; i++) {
if (memcmp(string_table + i, input, len) == 0 && string_table[i + len + 1] == 0) {
return string_table + i;
}
}
return nullptr;
}
uint8_t *add_string(uint8_t *input) {
// Try to find string in string_table.
// If it exists, return that address.
// Otherwise add it to the table.
// If the string can't fit into the table, grow the table before adding it.
// Note for future self/other people:
// The code doesn't really benefit from compressing the duplicate strings
// in the string table and it only slows it down greatly.
// The full chara and music database uses less than 1mb total so it's not an issue.
if (input == NULL) {
return NULL;
}
uint32_t len = strlen((char *)input);
if (len == 0) {
return NULL;
}
if (string_table == NULL || string_table_idx + len + 1 >= string_table_size) {
// Realloc array to hold more strings
uint32_t new_string_table_size = string_table_idx + len + 1 + string_table_growth_size;
uint8_t *string_table_new = (uint8_t *)realloc(string_table, new_string_table_size + 1);
if (string_table_new == NULL) {
printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n",
string_table_size, string_table_size + string_table_growth_size);
exit(1);
}
// Zero out new section
memset(string_table_new + string_table_size, 0, new_string_table_size - string_table_size);
string_table = string_table_new;
string_table_size = new_string_table_size;
}
uint8_t *output = string_table + string_table_idx;
memcpy(string_table + string_table_idx, input, len);
string_table[string_table_idx + len + 1] = 0;
string_table_idx += len + 1;
return output;
}
void add_chart_type_flag(uint32_t cur_idx, int8_t flag) { chart_type_overrides[cur_idx] = flag; }
int8_t get_chart_type_override(uint8_t *buffer, uint32_t music_idx, uint32_t chart_idx) {
music_entry *m = (music_entry *)buffer;
uint32_t idx = m[music_idx].charts[chart_idx];
if (chart_type_overrides.find(idx) == chart_type_overrides.end()) {
printf("Couldn't find chart\n");
return -1;
}
printf("Found chart: %d\n", chart_type_overrides[idx]);
if (chart_type_overrides[idx] == 1) {
// Uses new chart type
return 0;
} else if (chart_type_overrides[idx] == 0) {
// Does not use new chart
return 1;
}
return -1;
}
uint32_t get_lapis_shape_id(uint8_t *lapis_shape_ptr) {
if (!lapis_shape_ptr) {
return 0;
}
RET_COMPARE_INDEX(lapis_shape_ptr, "dia", 1)
RET_COMPARE_INDEX(lapis_shape_ptr, "tear", 2)
RET_COMPARE_INDEX(lapis_shape_ptr, "heart", 3)
RET_COMPARE_INDEX(lapis_shape_ptr, "squ", 4)
return 0;
}
uint32_t get_lapis_color_id(uint8_t *lapis_color_ptr) {
if (!lapis_color_ptr) {
return 0;
}
RET_COMPARE_INDEX(lapis_color_ptr, "blue", 1)
RET_COMPARE_INDEX(lapis_color_ptr, "pink", 2)
RET_COMPARE_INDEX(lapis_color_ptr, "red", 3)
RET_COMPARE_INDEX(lapis_color_ptr, "green", 4)
RET_COMPARE_INDEX(lapis_color_ptr, "normal", 5)
RET_COMPARE_INDEX(lapis_color_ptr, "yellow", 6)
RET_COMPARE_INDEX(lapis_color_ptr, "purple", 7)
RET_COMPARE_INDEX(lapis_color_ptr, "black", 8)
return 0;
}
uint16_t get_chara_idx(const uint8_t *chara_id) {
if (!chara_id || strlen((char *)chara_id) == 0) {
return 0;
}
// This table is limited so keep it as small as possible
for (int32_t i = 0; i <= max_chara_id; i++) {
character_entry *cur = (character_entry *)chara_table + i;
if (chara_id != NULL && cur->chara_id_ptr != NULL &&
strlen((char *)cur->chara_id_ptr) == strlen((char *)chara_id) &&
strcmp((char *)cur->chara_id_ptr, (char *)chara_id) == 0) {
return i;
}
}
return 0;
}
uint32_t chart_label_to_idx(uint8_t *chart_label) {
if (!chart_label) {
return 1;
}
RET_COMPARE_INDEX(chart_label, "ep", 0)
RET_COMPARE_INDEX(chart_label, "np", 1)
RET_COMPARE_INDEX(chart_label, "hp", 2)
RET_COMPARE_INDEX(chart_label, "op", 3)
RET_COMPARE_INDEX(chart_label, "bp_n", 4)
RET_COMPARE_INDEX(chart_label, "bn", 4)
RET_COMPARE_INDEX(chart_label, "bp_h", 5)
RET_COMPARE_INDEX(chart_label, "bh", 5)
RET_COMPARE_INDEX(chart_label, "0", 0)
RET_COMPARE_INDEX(chart_label, "1", 1)
RET_COMPARE_INDEX(chart_label, "2", 2)
RET_COMPARE_INDEX(chart_label, "3", 3)
RET_COMPARE_INDEX(chart_label, "4", 4)
RET_COMPARE_INDEX(chart_label, "5", 5)
return 1;
}
fontstyle_entry *get_fontstyle(int32_t cur_idx) {
if (cur_idx < 11) {
return NULL;
}
cur_idx -= 11;
if (cur_idx > fontmax_style_id) {
return NULL;
}
return (fontstyle_entry *)fontstyle_table + cur_idx;
}
character_entry *get_chara(int32_t cur_idx) {
if (used_chara_id.find(cur_idx) == used_chara_id.end()) {
return NULL;
}
return (character_entry *)chara_table + cur_idx;
}
music_entry *get_music(int32_t cur_idx) {
if (used_music_id.find(cur_idx) == used_music_id.end()) {
return NULL;
}
return (music_entry *)music_table + cur_idx;
}
chart_entry *get_chart(int32_t cur_idx) {
if (cur_idx > max_chart_id) {
return NULL;
}
return (chart_entry *)chart_table + cur_idx;
}
uint32_t add_style(int32_t cur_idx, uint32_t fontface, uint32_t color, uint32_t height,
uint32_t width) {
if (fontstyle_table == NULL || cur_idx + 1 >= (int)fontstyle_table_size) {
// Realloc array to hold more styles
uint32_t new_style_table_size = cur_idx + 1 + fontstyle_table_growth_size;
uint8_t *style_table_new = (uint8_t *)realloc(fontstyle_table, (new_style_table_size + 1) *
sizeof(fontstyle_entry));
if (style_table_new == NULL) {
printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n",
fontstyle_table_size, fontstyle_table_size + fontstyle_table_growth_size);
exit(1);
}
// Zero out new section
memset(style_table_new + fontstyle_table_size * sizeof(fontstyle_entry), 0,
(new_style_table_size - fontstyle_table_size) * sizeof(fontstyle_entry));
fontstyle_table = style_table_new;
fontstyle_table_size = new_style_table_size;
}
// This table is limited so keep it as small as possible
for (int32_t i = 0; i <= fontmax_style_id; i++) {
fontstyle_entry *cur = (fontstyle_entry *)fontstyle_table + i;
if (cur->fontface == fontface && cur->color == color && cur->height == height &&
cur->width == width) {
return i;
}
}
fontstyle_entry *ptr = (fontstyle_entry *)fontstyle_table + cur_idx;
memset(ptr, 0, sizeof(fontstyle_entry));
ptr->fontface = fontface;
ptr->color = color;
ptr->height = height;
ptr->width = width;
if (cur_idx > fontmax_style_id) {
fontmax_style_id = cur_idx;
}
return cur_idx;
}
uint32_t add_flavor(int32_t cur_idx, uint8_t *phrase1, uint8_t *phrase2, uint8_t *phrase3,
uint8_t *phrase4, uint8_t *phrase5, uint8_t *phrase6, uint8_t *birthday,
uint8_t chara1_birth_month, uint8_t chara2_birth_month,
uint8_t chara3_birth_month, uint8_t chara1_birth_date,
uint8_t chara2_birth_date, uint8_t chara3_birth_date, uint16_t style1,
bool style2_flag, uint16_t style3, uint32_t fontstyle_fontface,
uint32_t fontstyle_color, uint32_t fontstyle_height, uint32_t fontstyle_width) {
if (flavor_table == NULL || cur_idx + 1 >= (int)flavor_table_size) {
// Realloc array to hold more flavors
uint32_t new_flavor_table_size = cur_idx + 1 + flavor_table_growth_size;
uint8_t *flavor_table_new =
(uint8_t *)realloc(flavor_table, (new_flavor_table_size + 1) * sizeof(flavor_entry));
if (flavor_table_new == NULL) {
printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n",
flavor_table_size, flavor_table_size + flavor_table_growth_size);
exit(1);
}
// Zero out new section
memset(flavor_table_new + flavor_table_size * sizeof(flavor_entry), 0,
(new_flavor_table_size - flavor_table_size) * sizeof(flavor_entry));
flavor_table = flavor_table_new;
flavor_table_size = new_flavor_table_size;
}
// This table is limited so keep it as small as possible
for (int32_t i = 0; i < cur_idx; i++) {
flavor_entry *cur = (flavor_entry *)flavor_table + i;
fontstyle_entry *fs = get_fontstyle(cur->style2);
if (phrase1 != NULL && cur->phrase1 != NULL &&
strlen((char *)cur->phrase1) == strlen((char *)phrase1) &&
strcmp((char *)cur->phrase1, (char *)phrase1) == 0 && phrase2 != NULL &&
cur->phrase2 != NULL && strlen((char *)cur->phrase2) == strlen((char *)phrase2) &&
strcmp((char *)cur->phrase2, (char *)phrase2) == 0 && phrase3 != NULL &&
cur->phrase3 != NULL && strlen((char *)cur->phrase3) == strlen((char *)phrase3) &&
strcmp((char *)cur->phrase3, (char *)phrase3) == 0 && phrase4 != NULL &&
cur->phrase4 != NULL && strlen((char *)cur->phrase4) == strlen((char *)phrase4) &&
strcmp((char *)cur->phrase4, (char *)phrase4) == 0 && phrase5 != NULL &&
cur->phrase5 != NULL && strlen((char *)cur->phrase5) == strlen((char *)phrase5) &&
strcmp((char *)cur->phrase5, (char *)phrase5) == 0 && phrase6 != NULL &&
cur->phrase6 != NULL && strlen((char *)cur->phrase6) == strlen((char *)phrase6) &&
strcmp((char *)cur->phrase6, (char *)phrase6) == 0 && birthday != NULL &&
cur->birthday_ptr != NULL &&
strlen((char *)cur->birthday_ptr) == strlen((char *)birthday) &&
strcmp((char *)cur->birthday_ptr, (char *)birthday) == 0 &&
cur->chara1_birth_month == chara1_birth_month &&
cur->chara2_birth_month == chara2_birth_month &&
cur->chara3_birth_month == chara3_birth_month &&
cur->chara1_birth_date == chara1_birth_date &&
cur->chara2_birth_date == chara2_birth_date &&
cur->chara3_birth_date == chara3_birth_date && cur->style1 == style1 &&
cur->style3 == style3 &&
(fs == NULL // TODO: Would a NULL fontstyle actually be an error?
|| (fs != NULL && fs->fontface == fontstyle_fontface && fs->color == fontstyle_color &&
fs->height == fontstyle_height && fs->width == fontstyle_width))) {
return i;
}
}
uint16_t style2 = add_style(fontmax_style_id + 1, fontstyle_fontface, fontstyle_color,
fontstyle_height, fontstyle_width) +
11;
flavor_entry *ptr = (flavor_entry *)flavor_table + cur_idx;
memset(ptr, 0, sizeof(flavor_entry));
if (phrase1) {
memcpy(ptr->phrase1, phrase1, sizeof(ptr->phrase1) - 1);
}
if (phrase2) {
memcpy(ptr->phrase2, phrase2, sizeof(ptr->phrase2) - 1);
}
if (phrase3) {
memcpy(ptr->phrase3, phrase3, sizeof(ptr->phrase3) - 1);
}
if (phrase4) {
memcpy(ptr->phrase4, phrase4, sizeof(ptr->phrase4) - 1);
}
if (phrase5) {
memcpy(ptr->phrase5, phrase5, sizeof(ptr->phrase5) - 1);
}
if (phrase6) {
memcpy(ptr->phrase6, phrase6, sizeof(ptr->phrase6) - 1);
}
ptr->birthday_ptr = birthday;
ptr->chara1_birth_month = chara1_birth_month;
ptr->chara2_birth_month = chara2_birth_month;
ptr->chara3_birth_month = chara3_birth_month;
ptr->chara1_birth_date = chara1_birth_date;
ptr->chara2_birth_date = chara2_birth_date;
ptr->chara3_birth_date = chara3_birth_date;
ptr->style1 = style1;
ptr->style2 = style2;
ptr->style3 = style3;
if (cur_idx > max_flavor_id) {
max_flavor_id = cur_idx;
}
return cur_idx;
}
uint32_t add_chara(int32_t cur_idx, uint8_t *chara_id_ptr, uint32_t flags, uint8_t *folder_ptr,
uint8_t *gg_ptr, uint8_t *cs_ptr, uint8_t *icon1_ptr, uint8_t *icon2_ptr,
uint16_t chara_xw, uint16_t chara_yh, uint32_t display_flags, int16_t flavor_idx,
uint8_t chara_variation_num, uint8_t *sort_name_ptr, uint8_t *disp_name_ptr,
uint32_t file_type, uint32_t lapis_shape, uint8_t lapis_color, uint8_t *ha_ptr,
uint8_t *catchtext_ptr, int16_t win2_trigger, uint32_t game_version) {
if (chara_table == NULL || cur_idx + 1 >= (int32_t)chara_table_size) {
// Realloc array to hold more charas
uint32_t new_chara_table_size = cur_idx + 1 + chara_table_growth_size;
uint8_t *chara_table_new =
(uint8_t *)realloc(chara_table, (new_chara_table_size + 1) * sizeof(character_entry));
if (chara_table_new == NULL) {
printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n",
chara_table_size, chara_table_size + chara_table_growth_size);
exit(1);
}
// Zero out new section
memset(chara_table_new + chara_table_size * sizeof(character_entry), 0,
(new_chara_table_size - chara_table_size) * sizeof(character_entry));
// Initialize new entries to sane defaults
for (uint32_t i = chara_table_size; i < new_chara_table_size; i++) {
character_entry *chara_entry = (character_entry *)chara_table_new + i;
memset(chara_entry, 0, sizeof(*chara_entry));
chara_entry->chara_id_ptr = add_string((uint8_t *)"bamb_1a");
chara_entry->flags = 0x31;
chara_entry->folder_ptr = add_string((uint8_t *)"22");
chara_entry->gg_ptr = add_string((uint8_t *)"gg_bamb_1a");
chara_entry->cs_ptr = add_string((uint8_t *)"cs_bamb_1a");
chara_entry->icon1_ptr = add_string((uint8_t *)"cs_a");
chara_entry->icon2_ptr = add_string((uint8_t *)"cs_b");
chara_entry->chara_xw = 240;
chara_entry->chara_yh = 220;
chara_entry->file_type = 53;
chara_entry->lapis_shape = get_lapis_shape_id((uint8_t *)"dia");
chara_entry->lapis_color = get_lapis_color_id((uint8_t *)"yellow");
chara_entry->win2_trigger = -1;
}
chara_table = chara_table_new;
chara_table_size = new_chara_table_size;
}
character_entry *ptr = (character_entry *)chara_table + cur_idx;
ptr->chara_id_ptr = chara_id_ptr;
ptr->flags = flags;
ptr->folder_ptr = folder_ptr;
ptr->gg_ptr = gg_ptr;
ptr->cs_ptr = cs_ptr;
ptr->icon1_ptr = icon1_ptr;
ptr->icon2_ptr = icon2_ptr;
ptr->chara_xw = chara_xw;
ptr->chara_yh = chara_yh;
ptr->display_flags = display_flags;
ptr->flavor_idx = flavor_idx;
ptr->chara_variation_num = chara_variation_num;
ptr->sort_name_ptr = sort_name_ptr;
ptr->disp_name_ptr = disp_name_ptr;
ptr->file_type = file_type;
ptr->lapis_shape = lapis_shape;
ptr->lapis_color = lapis_color;
ptr->ha_ptr = ha_ptr;
ptr->catchtext_ptr = catchtext_ptr ? catchtext_ptr : chara_table_nullptr;
ptr->win2_trigger = win2_trigger;
ptr->game_version = game_version;
if (cur_idx > max_chara_id) {
max_chara_id = cur_idx;
}
used_chara_id[cur_idx] = true;
return cur_idx;
}
uint32_t add_music(int32_t cur_idx, uint8_t *fw_genre_ptr, uint8_t *fw_title_ptr,
uint8_t *fw_artist_ptr, uint8_t *genre_ptr, uint8_t *title_ptr,
uint8_t *artist_ptr, uint16_t chara1, uint16_t chara2, uint32_t mask,
uint32_t folder, uint32_t cs_version, uint32_t categories, uint8_t *diffs,
uint16_t *charts, uint8_t *ha_ptr, uint32_t chara_x, uint32_t chara_y,
uint16_t *unk1, uint16_t *display_bpm, uint8_t *hold_flags, bool allow_resize) {
if (allow_resize && (music_table == NULL || cur_idx + 1 >= (int32_t)music_table_size)) {
// Realloc array to hold more musics
uint32_t new_music_table_size = cur_idx + 1 + music_table_growth_size;
uint8_t *music_table_new =
(uint8_t *)realloc(music_table, (new_music_table_size + 1) * sizeof(music_entry));
if (music_table_new == NULL) {
printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n",
music_table_size, music_table_size + music_table_growth_size);
exit(1);
}
// Initialize new entries to sane defaults
for (uint32_t i = music_table_size; i < new_music_table_size; i++) {
music_entry *e = (music_entry *)music_table_new + i;
memset(e, 0, sizeof(*e));
e->fw_genre_ptr = filler_str;
e->fw_title_ptr = filler_str;
e->fw_artist_ptr = filler_str;
e->genre_ptr = filler_str;
e->title_ptr = filler_str;
e->artist_ptr = filler_str;
e->mask = 0x80000000;
e->folder = 25;
memset((char *)e->diffs, 1, sizeof(e->diffs));
}
music_table = music_table_new;
music_table_size = new_music_table_size;
}
music_entry *ptr = (music_entry *)music_table + cur_idx;
ptr->fw_genre_ptr = fw_genre_ptr;
ptr->fw_title_ptr = fw_title_ptr;
ptr->fw_artist_ptr = fw_artist_ptr;
ptr->genre_ptr = genre_ptr;
ptr->title_ptr = title_ptr;
ptr->artist_ptr = artist_ptr;
ptr->chara1 = chara1;
ptr->chara2 = chara2;
ptr->mask = mask;
ptr->folder = folder;
ptr->cs_version = cs_version;
ptr->categories = categories;
memcpy(ptr->diffs, (char *)diffs, sizeof(uint8_t) * 6);
memcpy(ptr->charts, charts, sizeof(uint16_t) * 7);
ptr->ha_ptr = ha_ptr;
ptr->chara_x = chara_x;
ptr->chara_y = chara_y;
memcpy(ptr->unk1, unk1, sizeof(uint16_t) * 32);
memcpy(ptr->display_bpm, display_bpm, sizeof(uint16_t) * 12);
memcpy(ptr->hold_flags, hold_flags, sizeof(uint8_t) * 8);
if (cur_idx > max_music_id) {
max_music_id = cur_idx;
}
used_music_id[cur_idx] = true;
return cur_idx;
}
uint32_t add_chart(int32_t cur_idx, uint8_t *folder, uint8_t *filename, int32_t audio_param1,
int32_t audio_param2, int32_t audio_param3, int32_t audio_param4,
uint32_t file_type, uint16_t used_keys, bool override_idx) {
if (chart_table == NULL || cur_idx + 1 >= (int32_t)chart_table_size) {
// Realloc array to hold more charts
uint32_t new_chart_table_size = cur_idx + 1 + chart_table_growth_size;
uint8_t *chart_table_new =
(uint8_t *)realloc(chart_table, (new_chart_table_size + 1) * sizeof(chart_entry));
if (chart_table_new == NULL) {
printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n",
chart_table_size, chart_table_size + chart_table_growth_size);
exit(1);
}
// Initialize new entries to sane defaults
for (uint32_t i = chart_table_size; i < new_chart_table_size; i++) {
chart_entry *e = (chart_entry *)chart_table_new + i;
memset(e, 0, sizeof(*e));
}
chart_table = chart_table_new;
chart_table_size = new_chart_table_size;
}
// This table is limited so keep it as small as possible
for (int32_t i = 0; !override_idx && i <= max_chart_id; i++) {
chart_entry *cur = (chart_entry *)chart_table + i;
if (folder != NULL && cur->folder_ptr != NULL &&
strlen((char *)cur->folder_ptr) == strlen((char *)folder) &&
strcmp((char *)cur->folder_ptr, (char *)folder) == 0 && filename != NULL &&
cur->filename_ptr != NULL &&
strlen((char *)cur->filename_ptr) == strlen((char *)filename) &&
strcmp((char *)cur->filename_ptr, (char *)filename) == 0 &&
cur->audio_param1 == audio_param1 && cur->audio_param2 == audio_param2 &&
cur->audio_param3 == audio_param3 && cur->audio_param4 == audio_param4 &&
cur->file_type == file_type && cur->used_keys == used_keys) {
return i;
}
}
chart_entry *ptr = (chart_entry *)chart_table + cur_idx;
ptr->folder_ptr = folder;
ptr->filename_ptr = filename;
ptr->audio_param1 = audio_param1;
ptr->audio_param2 = audio_param2;
ptr->audio_param3 = audio_param3;
ptr->audio_param4 = audio_param4;
ptr->file_type = file_type;
ptr->used_keys = used_keys;
if (cur_idx > max_chart_id) {
max_chart_id = cur_idx;
}
return cur_idx;
}
void parse_charadb(const char *input_filename, const char *target) {
if (!file_exists(input_filename)) {
printf("Couldn't find %s, skipping...\n", input_filename);
return;
}
property *config_xml = load_prop_file(input_filename);
if (target && strlen(target) > 0) {
char beforeTarget[64] = {};
property_node_refer(config_xml, property_search(config_xml, NULL, "/database"), "before@",
PROPERTY_TYPE_ATTR, beforeTarget, 64);
if (strlen(beforeTarget) > 0 && atoi(target) >= atoi(beforeTarget)) {
printf("Currently loading %s, found database that is only valid before %s, skipping %s...\n", target, beforeTarget, input_filename);
return;
}
char afterTarget[64] = {};
property_node_refer(config_xml, property_search(config_xml, NULL, "/database"), "after@",
PROPERTY_TYPE_ATTR, afterTarget, 64);
if (strlen(afterTarget) > 0 && atoi(target) < atoi(afterTarget)) {
printf("Currently loading %s, found database that is only valid after %s, skipping %s...\n", target, afterTarget, input_filename);
return;
}
}
property_node *prop = NULL;
if ((prop = property_search(config_xml, NULL, "/database/chara"))) {
// Iterate over all charas in /database
for (; prop != NULL; prop = property_node_traversal(prop, TRAVERSE_NEXT_SEARCH_RESULT)) {
char idxStr[256] = {};
property_node_refer(config_xml, prop, "id@", PROPERTY_TYPE_ATTR, idxStr,
sizeof(idxStr));
uint32_t idx = atoi(idxStr);
// Get an existing music entry in memory
// If it exists, return the existing entry
// If it doesn't exist, create a new entry in memory
// Update the data in-place and make all parameters optional
character_entry *c = get_chara(idx);
bool is_fresh = c == NULL;
if (is_fresh) {
// Default character entry
// TODO: Is there a better way I can make it force a new entry without having to
// define everything like this?
add_chara(idx, add_string((uint8_t *)"bamb_1a"), 0x31, add_string((uint8_t *)"22"),
add_string((uint8_t *)"gg_bamb_1a"), add_string((uint8_t *)"cs_bamb_1a"),
add_string((uint8_t *)"cs_a"), add_string((uint8_t *)"cs_b"), 240, 220, 0,
0, 0, NULL, NULL, 53, get_lapis_shape_id((uint8_t *)"dia"),
get_lapis_color_id((uint8_t *)"yellow"), NULL, NULL, -1, 0);
c = get_chara(idx);
}
DWORD old_prot;
VirtualProtect((LPVOID)c, sizeof(character_entry), PAGE_EXECUTE_READWRITE, &old_prot);
// Save to character table
READ_STR_OPT(config_xml, prop, "chara_id", c->chara_id_ptr)
READ_U32_OPT(config_xml, prop, "flags", c->flags)
READ_STR_OPT(config_xml, prop, "folder", c->folder_ptr)
READ_STR_OPT(config_xml, prop, "gg", c->gg_ptr)
READ_STR_OPT(config_xml, prop, "cs", c->cs_ptr)
READ_STR_OPT(config_xml, prop, "icon1", c->icon1_ptr)
READ_STR_OPT(config_xml, prop, "icon2", c->icon2_ptr)
READ_U16_OPT(config_xml, prop, "chara_xw", c->chara_xw)
READ_U16_OPT(config_xml, prop, "chara_yh", c->chara_yh)
READ_U32_OPT(config_xml, prop, "display_flags", c->display_flags)
READ_U8_OPT(config_xml, prop, "chara_variation_num", c->chara_variation_num)
READ_STR_OPT(config_xml, prop, "sort_name", c->sort_name_ptr)
READ_STR_OPT(config_xml, prop, "disp_name", c->disp_name_ptr)
READ_U32_OPT(config_xml, prop, "file_type", c->file_type)
READ_LAPIS_SHAPE_OPT(config_xml, prop, "lapis_shape", c->lapis_shape)
READ_LAPIS_COLOR_OPT(config_xml, prop, "lapis_color", c->lapis_color)
READ_STR_OPT(config_xml, prop, "ha", c->ha_ptr)
READ_STR_OPT(config_xml, prop, "catchtext", c->catchtext_ptr)
READ_S16_OPT(config_xml, prop, "win2_trigger", c->win2_trigger)
READ_U32_OPT(config_xml, prop, "game_version", c->game_version)
property_node *prop_flavor = NULL;
int32_t flavor_idx = -1;
if ((prop_flavor = property_search(config_xml, prop, "/flavor"))) {
// Save to flavor table
READ_STR(config_xml, prop_flavor, "phrase1", phrase1, phrase1_ptr)
READ_STR(config_xml, prop_flavor, "phrase2", phrase2, phrase2_ptr)
READ_STR(config_xml, prop_flavor, "phrase3", phrase3, phrase3_ptr)
READ_STR(config_xml, prop_flavor, "phrase4", phrase4, phrase4_ptr)
READ_STR(config_xml, prop_flavor, "phrase5", phrase5, phrase5_ptr)
READ_STR(config_xml, prop_flavor, "phrase6", phrase6, phrase6_ptr)
READ_STR(config_xml, prop_flavor, "birthday", birthday, birthday_ptr)
READ_U8(config_xml, prop_flavor, "chara1_birth_month", chara1_birth_month)
READ_U8(config_xml, prop_flavor, "chara2_birth_month", chara2_birth_month)
READ_U8(config_xml, prop_flavor, "chara3_birth_month", chara3_birth_month)
READ_U8(config_xml, prop_flavor, "chara1_birth_date", chara1_birth_date)
READ_U8(config_xml, prop_flavor, "chara2_birth_date", chara2_birth_date)
READ_U8(config_xml, prop_flavor, "chara3_birth_date", chara3_birth_date)
READ_U16(config_xml, prop_flavor, "style1", style1)
READ_U16(config_xml, prop_flavor, "style3", style3)
property_node *prop_flavor_style = NULL;
bool style2_flag = false;
uint32_t fontface = 0, color = 0, height = 0, width = 0;
if ((prop_flavor_style = property_search(config_xml, prop_flavor, "/style2"))) {
// Save to style table
READ_U32_OPT(config_xml, prop_flavor_style, "fontface", fontface)
READ_U32_OPT(config_xml, prop_flavor_style, "color", color)
READ_U32_OPT(config_xml, prop_flavor_style, "height", height)
READ_U32_OPT(config_xml, prop_flavor_style, "width", width)
style2_flag = true;
// printf("style[%d] idx[%d]\n", style_table_ptr, style2_idx);
}
flavor_idx = add_flavor(
max_flavor_id + 1, phrase1_ptr, phrase2_ptr, phrase3_ptr, phrase4_ptr,
phrase5_ptr, phrase6_ptr, birthday_ptr, chara1_birth_month, chara2_birth_month,
chara3_birth_month, chara1_birth_date, chara2_birth_date, chara3_birth_date,
style1, style2_flag, style3, fontface, color, height, width);
// printf("flavor[%d] idx[%d]\n", max_flavor_id, flavor_idx);
if (flavor_idx > (int32_t)flavor_limit) {
// This is a hack because I don't feel like finding the proper buffers to patch
// for this
printf("WARNING: Overflowed flavor text table limit of %d, defaulting to index "
"0 instead of %d\n",
flavor_limit, flavor_idx);
flavor_idx = -1;
}
c->flavor_idx = flavor_idx;
}
if ((c->flavor_idx == 0 || c->flavor_idx == -1)) {
c->flavor_idx = 1;
printf("Setting default flavor for chara id %d\n", idx);
}
VirtualProtect((LPVOID)c, sizeof(character_entry), old_prot, &old_prot);
}
}
free(config_xml);
}
void parse_musicdb(const char *input_filename, const char *target) {
if (!file_exists(input_filename)) {
printf("Couldn't find %s, skipping...\n", input_filename);
return;
}
property *config_xml = load_prop_file(input_filename);
if (target && strlen(target) > 0) {
char beforeTarget[64] = {};
property_node_refer(config_xml, property_search(config_xml, NULL, "/database"), "before@",
PROPERTY_TYPE_ATTR, beforeTarget, 64);
if (strlen(beforeTarget) > 0 && atoi(target) >= atoi(beforeTarget)) {
printf("Currently loading %s, found database that is only valid before %s, skipping %s...\n", target, beforeTarget, input_filename);
return;
}
char afterTarget[64] = {};
property_node_refer(config_xml, property_search(config_xml, NULL, "/database"), "after@",
PROPERTY_TYPE_ATTR, afterTarget, 64);
if (strlen(afterTarget) > 0 && atoi(target) < atoi(afterTarget)) {
printf("Currently loading %s, found database that is only valid after %s, skipping %s...\n", target, afterTarget, input_filename);
return;
}
}
property_node *prop = NULL;
if ((prop = property_search(config_xml, NULL, "/database/music"))) {
// Iterate over all musics in /database
for (; prop != NULL; prop = property_node_traversal(prop, TRAVERSE_NEXT_SEARCH_RESULT)) {
char idxStr[256] = {};
property_node_refer(config_xml, prop, "id@", PROPERTY_TYPE_ATTR, idxStr,
sizeof(idxStr));
uint32_t idx = atoi(idxStr);
// Get an existing music entry in memory
// If it exists, return the existing entry
// If it doesn't exist, create a new entry in memory
// Update the data in-place and make all parameters optional
music_entry *m = get_music(idx);
bool is_fresh = m == NULL;
if (is_fresh) {
// Default music entry
static const uint8_t diffs[6] = {0};
static const uint16_t charts[7] = {0};
static const uint16_t unk1[32] = {0};
static const uint16_t display_bpm[12] = {0};
static const uint8_t hold_flags[8] = {0};
// TODO: Is there a better way I can make it force a new entry without having to
// define everything like this?
add_music(idx, filler_str, filler_str, filler_str, filler_str, filler_str,
filler_str, 0, 0, 0x80000000, 25, 0, 0, (uint8_t *)&diffs[0],
(uint16_t *)&charts[0], NULL, 0, 0, (uint16_t *)&unk1[0],
(uint16_t *)&display_bpm[0], (uint8_t *)&hold_flags[0], true);
m = get_music(idx);
}
DWORD old_prot;
VirtualProtect((LPVOID)m, sizeof(music_entry), PAGE_EXECUTE_READWRITE, &old_prot);
READ_STR_OPT(config_xml, prop, "fw_genre", m->fw_genre_ptr)
READ_STR_OPT(config_xml, prop, "fw_title", m->fw_title_ptr)
READ_STR_OPT(config_xml, prop, "fw_artist", m->fw_artist_ptr)
READ_STR_OPT(config_xml, prop, "genre", m->genre_ptr)
READ_STR_OPT(config_xml, prop, "title", m->title_ptr)
READ_STR_OPT(config_xml, prop, "artist", m->artist_ptr)
READ_CHARA_OPT(config_xml, prop, "chara1", m->chara1)
READ_CHARA_OPT(config_xml, prop, "chara2", m->chara2)
uint32_t orig_mask = m->mask;
READ_U32_OPT(config_xml, prop, "mask", m->mask)
READ_U32_OPT(config_xml, prop, "folder", m->folder)
READ_U32_OPT(config_xml, prop, "cs_version", m->cs_version)
READ_U32_OPT(config_xml, prop, "categories", m->categories)
READ_STR_OPT(config_xml, prop, "ha", m->ha_ptr)
READ_U32_OPT(config_xml, prop, "chara_x", m->chara_x)
READ_U32_OPT(config_xml, prop, "chara_y", m->chara_y)
READ_U16_ARR_OPT(config_xml, prop, "unk1", m->unk1, 32)
READ_U16_ARR_OPT(config_xml, prop, "display_bpm", m->display_bpm, 16)
property_node *prop_chart = NULL;
int32_t default_chart_idx = -1;
const uint32_t CHART_MASKS[] = {0x00080000, 0, 0x01000000,
0x02000000, 0, 0x04000000};
// Copy old chart flags from the previous mask
for (size_t i = 0; i < 6; i++) {
if ((orig_mask & CHART_MASKS[i]) != 0) {
m->mask |= CHART_MASKS[i];
}
}
if ((prop_chart = property_search(config_xml, prop, "charts/chart"))) {
for (; prop_chart != NULL; prop_chart = property_node_traversal(
prop_chart, TRAVERSE_NEXT_SEARCH_RESULT)) {
char chartIdxStr[256] = {};
property_node_refer(config_xml, prop_chart, "idx@", PROPERTY_TYPE_ATTR,
chartIdxStr, sizeof(chartIdxStr));
uint32_t chart_idx = chart_label_to_idx((uint8_t *)chartIdxStr);
chart_entry *c2 = get_chart(m->charts[chart_idx]);
if (is_fresh || c2 == NULL) {
m->charts[chart_idx] = add_chart(max_chart_id + 1, filler_str, filler_str,
0, 0, 0, 0, 0, 0, true);
} else {
m->charts[chart_idx] =
add_chart(max_chart_id + 1, c2->folder_ptr, c2->filename_ptr,
c2->audio_param1, c2->audio_param2, c2->audio_param3,
c2->audio_param4, c2->file_type, c2->used_keys, true);
}
chart_entry *c = get_chart(m->charts[chart_idx]);
if (!c) {
printf("Couldn't find chart entry!\n");
exit(1);
}
DWORD old_prot_chart;
VirtualProtect((LPVOID)c, sizeof(chart_entry), PAGE_EXECUTE_READWRITE, &old_prot_chart);
READ_STR_OPT(config_xml, prop_chart, "folder", c->folder_ptr)
READ_STR_OPT(config_xml, prop_chart, "filename", c->filename_ptr)
READ_S32_OPT(config_xml, prop_chart, "audio_param1", c->audio_param1)
READ_S32_OPT(config_xml, prop_chart, "audio_param2", c->audio_param2)
READ_S32_OPT(config_xml, prop_chart, "audio_param3", c->audio_param3)
READ_S32_OPT(config_xml, prop_chart, "audio_param4", c->audio_param4)
READ_U32_OPT(config_xml, prop_chart, "file_type", c->file_type)
READ_U16_OPT(config_xml, prop_chart, "used_keys", c->used_keys)
READ_U8_OPT(config_xml, prop_chart, "diff", m->diffs[chart_idx])
READ_U8_OPT(config_xml, prop_chart, "hold_flag", m->hold_flags[chart_idx])
if (property_search(config_xml, prop_chart, "force_new_chart_format")) {
READ_U32(config_xml, prop_chart, "force_new_chart_format",
force_new_chart_format)
add_chart_type_flag(m->charts[chart_idx], force_new_chart_format);
} else {
add_chart_type_flag(m->charts[chart_idx], m->hold_flags[chart_idx] == 1);
}
m->mask |= CHART_MASKS[chart_idx];
if (chart_idx == 1) {
default_chart_idx = m->charts[chart_idx];
}
VirtualProtect((LPVOID)c, sizeof(chart_entry), old_prot_chart, &old_prot_chart);
}
}
// Make sure that all entries in the charts array have a value.
if (m->charts[1] == 0 && default_chart_idx == -1) {
// A normal chart wasn't specified and one didn't originally exist.
// This will probably lead to the preview music not being the right preview.
for (int i = 0; i < 6; i++) {
if (m->charts[i] != 0) {
m->charts[1] = m->charts[i];
break;
}
}
}
VirtualProtect((LPVOID)m, sizeof(music_entry), old_prot, &old_prot);
}
}
free(config_xml);
}
void load_databases(const char *target_datecode) {
SearchFile s;
printf("XML db files search...\n");
s.search("data_mods", "xml", true);
auto result = s.getResult();
// Character databases must be loaded before music databases because the music databases could reference modified/new characters
for(uint16_t i=0;i<result.size();i++)
{
if ( strstr(result[i].c_str(), "charadb") == NULL )
continue;
printf("(charadb) Loading %s...\n", result[i].c_str());
parse_charadb(result[i].c_str(), target_datecode);
}
for(uint16_t i=0;i<result.size();i++)
{
if ( strstr(result[i].c_str(), "musicdb") == NULL )
continue;
printf("(musicdb) Loading %s...\n", result[i].c_str());
parse_musicdb(result[i].c_str(), target_datecode);
}
}
void musichax_core_init(bool force_unlocks, bool is_expansion_allowed, bool is_redirection_allowed,
char *target_datecode,
char *base_data,
uint64_t music_size, uint64_t *new_music_size, char *orig_music_data,
uint8_t **new_music_table,
uint64_t chart_size, uint64_t *new_chart_size, char *orig_chart_data,
uint8_t **new_chart_table,
uint64_t style_size, uint64_t *new_style_size, char *orig_style_data,
uint8_t **new_style_table,
uint64_t flavor_size, uint64_t *new_flavor_size, char *orig_flavor_data,
uint8_t **new_flavor_table,
uint64_t chara_size, uint64_t *new_chara_size, char *orig_chara_data,
uint8_t **new_chara_table) {
if (style_size > fontstyle_table_size) {
fontstyle_table_size = style_size;
}
if (flavor_size > flavor_table_size) {
flavor_table_size = flavor_size;
}
if (chart_size > chart_table_size) {
chart_table_size = chart_size;
}
if (music_size > music_table_size) {
music_table_size = music_size;
}
if (chara_size > chara_table_size) {
chara_table_size = chara_size;
}
flavor_limit = flavor_size;
fontstyle_table = (uint8_t *)calloc(fontstyle_table_size, sizeof(fontstyle_entry));
flavor_table = (uint8_t *)calloc(flavor_table_size, sizeof(flavor_entry));
chara_table = (uint8_t *)calloc(chara_table_size, sizeof(character_entry));
music_table = (uint8_t *)calloc(music_table_size, sizeof(music_entry));
chart_table = (uint8_t *)calloc(chart_table_size * 25, sizeof(chart_entry));
// Copy style table for use in the flavor table
printf("Copying style table\n");
for (uint32_t idx = 0; idx < style_size; idx++) {
fontstyle_entry *cur = (fontstyle_entry *)orig_style_data + idx;
add_style(idx, cur->fontface, cur->color, cur->height, cur->width);
}
// Copy flavor table for use in the character database
printf("Copying flavor table\n");
for (uint32_t idx = 0; idx < flavor_size; idx++) {
flavor_entry *cur = (flavor_entry *)orig_flavor_data + idx;
fontstyle_entry *cur_style = (fontstyle_entry *)orig_style_data + (cur->style2 - 11);
add_flavor(
idx, cur->phrase1, cur->phrase2, cur->phrase3, cur->phrase4, cur->phrase5, cur->phrase6,
cur->birthday_ptr != NULL
? add_string(cur->birthday_ptr)
: NULL,
cur->chara1_birth_month, cur->chara2_birth_month, cur->chara3_birth_month,
cur->chara1_birth_date, cur->chara2_birth_date, cur->chara3_birth_date, cur->style1,
true, cur->style3, cur_style->fontface, cur_style->color, cur_style->height,
cur_style->width);
}
// Copy character database for use in the music database
printf("Copying character database\n");
for (uint32_t idx = 0; idx < chara_size; idx++) {
character_entry *cur = (character_entry *)orig_chara_data + idx;
add_chara(
idx,
cur->chara_id_ptr != NULL
? add_string(cur->chara_id_ptr)
: NULL,
cur->flags,
cur->folder_ptr != NULL
? add_string(cur->folder_ptr)
: NULL,
cur->gg_ptr != NULL
? add_string(cur->gg_ptr)
: NULL,
cur->cs_ptr != NULL
? add_string(cur->cs_ptr)
: NULL,
cur->icon1_ptr != NULL
? add_string(cur->icon1_ptr)
: NULL,
cur->icon2_ptr != NULL
? add_string(cur->icon2_ptr)
: NULL,
cur->chara_xw, cur->chara_yh, cur->display_flags, cur->flavor_idx,
cur->chara_variation_num,
cur->sort_name_ptr != NULL
? add_string(cur->sort_name_ptr)
: NULL,
cur->disp_name_ptr != NULL
? add_string(cur->disp_name_ptr)
: NULL,
cur->file_type, cur->lapis_shape, cur->lapis_color,
cur->ha_ptr != NULL
? add_string(cur->ha_ptr)
: NULL,
cur->catchtext_ptr != NULL
? add_string(cur->catchtext_ptr)
: NULL,
cur->win2_trigger, cur->game_version);
}
// Copy music database
printf("Copying music database\n");
for (uint32_t idx = 0; idx < music_size; idx++) {
music_entry *cur = (music_entry *)orig_music_data + idx;
uint16_t charts[7] = {0};
for (int i = 0; i < 7; i++) {
chart_entry *cur_chart = (chart_entry *)orig_chart_data + cur->charts[i];
charts[i] = add_chart(cur->charts[i],
cur_chart->folder_ptr != NULL
? add_string(cur_chart->folder_ptr)
: NULL,
cur_chart->filename_ptr != NULL
? add_string(cur_chart->filename_ptr)
: NULL,
cur_chart->audio_param1, cur_chart->audio_param2,
cur_chart->audio_param3, cur_chart->audio_param4,
cur_chart->file_type, cur_chart->used_keys, true);
}
add_music(
idx,
cur->fw_genre_ptr != NULL
? add_string(cur->fw_genre_ptr)
: NULL,
cur->fw_title_ptr != NULL
? add_string(cur->fw_title_ptr)
: NULL,
cur->fw_artist_ptr != NULL
? add_string(cur->fw_artist_ptr)
: NULL,
cur->genre_ptr != NULL
? add_string(cur->genre_ptr)
: NULL,
cur->title_ptr != NULL
? add_string(cur->title_ptr)
: NULL,
cur->artist_ptr != NULL
? add_string(cur->artist_ptr)
: NULL,
cur->chara1, cur->chara2, cur->mask, cur->folder, cur->cs_version, cur->categories,
cur->diffs, charts,
cur->ha_ptr != NULL
? add_string(cur->ha_ptr)
: NULL,
cur->chara_x, cur->chara_y, cur->unk1, cur->display_bpm, cur->hold_flags, true);
}
load_databases((const char *)target_datecode);
// Add some filler charts to fix some bugs (hack)
for (int i = 0; i < 10; i++) {
add_chart(max_chart_id + i + 1, NULL, NULL, 0, 0, 0, 0, 0, 0, true);
}
// Add one extra song as padding
{
if (max_music_id > (int64_t)music_size) {
static const uint8_t diffs[6] = {0};
static const uint16_t charts[7] = {0};
static const uint16_t unk1[32] = {0};
static const uint16_t display_bpm[12] = {0};
static const uint8_t hold_flags[8] = {0};
add_music(max_music_id + 1, filler_str, filler_str, filler_str, filler_str, filler_str,
filler_str, 0, 0, 0x80000000, 25, 0, 0, (uint8_t *)&diffs[0],
(uint16_t *)&charts[0], NULL, 0, 0, (uint16_t *)&unk1[0],
(uint16_t *)&display_bpm[0], (uint8_t *)&hold_flags[0], true);
}
if (max_chara_id > (int64_t)chara_size) {
add_chara(max_chara_id + 1, add_string((uint8_t *)"bamb_1a"), 0x31, add_string((uint8_t *)"22"),
add_string((uint8_t *)"gg_bamb_1a"), add_string((uint8_t *)"cs_bamb_1a"),
add_string((uint8_t *)"cs_a"), add_string((uint8_t *)"cs_b"), 240, 220, 0,
0, 0, NULL, NULL, 53, get_lapis_shape_id((uint8_t *)"dia"),
get_lapis_color_id((uint8_t *)"yellow"), NULL, NULL, -1, 0);
}
}
if (!is_expansion_allowed) {
max_music_id = music_size;
max_chara_id = chara_size;
fontmax_style_id = style_size;
max_flavor_id = flavor_size;
max_chart_id = chart_size;
}
if (is_redirection_allowed) {
*new_music_table = music_table;
*new_chara_table = chara_table;
*new_style_table = fontstyle_table;
*new_flavor_table = flavor_table;
*new_chart_table = chart_table;
} else {
// Copy new buffer data over top original buffers
// EXPERIMENTAL!
// Not really a supported feature, but I added it just in case someone wants to replace data
// in-place
patch_memory((uint64_t)orig_music_data, (char *)music_table,
sizeof(music_entry) * music_size);
patch_memory((uint64_t)orig_chart_data, (char *)chart_table,
sizeof(chart_entry) * chart_size);
patch_memory((uint64_t)orig_style_data, (char *)fontstyle_table,
sizeof(fontstyle_entry) * style_size);
patch_memory((uint64_t)orig_flavor_data, (char *)flavor_table,
sizeof(flavor_entry) * flavor_size);
patch_memory((uint64_t)orig_chara_data, (char *)chara_table,
sizeof(character_entry) * chara_size);
*new_music_table = (uint8_t *)orig_music_data;
*new_chara_table = (uint8_t *)orig_chara_data;
*new_style_table = (uint8_t *)orig_style_data;
*new_flavor_table = (uint8_t *)orig_flavor_data;
*new_chart_table = (uint8_t *)orig_chart_data;
}
*new_music_size = max_music_id;
*new_chara_size = max_chara_id;
*new_style_size = fontmax_style_id;
*new_flavor_size = max_flavor_id;
*new_chart_size = max_chart_id;
if (force_unlocks) {
music_entry *m = (music_entry *)*new_music_table;
for (uint64_t i = 0; i < *new_music_size; i++) {
uint32_t new_mask = m[i].mask & ~0x8000080;
if (m[i].title_ptr != NULL && new_mask != m[i].mask) {
printf("Unlocking [%04lld] %s... %08x -> %08x\n", i, m[i].title_ptr, m[i].mask,
new_mask);
patch_memory((uint64_t)&m[i].mask, (char *)&new_mask, sizeof(uint32_t));
}
}
character_entry *c = (character_entry *)*new_chara_table;
for (uint64_t i = 0; i < *new_chara_size; i++) {
uint32_t new_flags = c[i].flags & ~3;
if (new_flags != c[i].flags && c[i].disp_name_ptr != NULL && strlen((char*)c[i].disp_name_ptr) > 0) {
printf("Unlocking [%04lld] %s... %08x -> %08x\n", i, c[i].disp_name_ptr, c[i].flags, new_flags);
patch_memory((uint64_t)&c[i].flags, (char *)&new_flags, sizeof(uint32_t));
if ((c[i].flavor_idx == 0 || c[i].flavor_idx == -1)) {
int flavor_idx = 1;
patch_memory((uint64_t)&c[i].flavor_idx, (char *)&flavor_idx, sizeof(uint32_t));
printf("Setting default flavor for chara id %lld\n", i);
}
}
}
}
}