mirror of
https://github.com/korenkonder/ReDIVA.git
synced 2026-10-01 19:28:02 +03:00
Better curve interpolation and curve fitting. Upped cap for AetMgr and SprSet sets
960 lines
32 KiB
C++
960 lines
32 KiB
C++
/*
|
|
by korenkonder
|
|
GitHub/GitLab: korenkonder
|
|
*/
|
|
|
|
#include "sprite.hpp"
|
|
#include "../f2/struct.hpp"
|
|
#include "../io/file_stream.hpp"
|
|
#include "../io/memory_stream.hpp"
|
|
#include "../io/path.hpp"
|
|
#include "../hash.hpp"
|
|
#include "../sort.hpp"
|
|
#include "../str_utils.hpp"
|
|
|
|
static void sprite_database_file_classic_read_inner(sprite_database_file* spr_db, stream& s);
|
|
static void sprite_database_file_classic_write_inner(sprite_database_file* spr_db, stream& s);
|
|
static void sprite_database_file_modern_read_inner(sprite_database_file* spr_db, stream& s, uint32_t header_length);
|
|
static void sprite_database_file_modern_write_inner(sprite_database_file* spr_db, stream& s);
|
|
static int64_t sprite_database_file_strings_get_string_offset(std::vector<string_hash>& vec,
|
|
std::vector<int64_t>& vec_off, std::string& str);
|
|
static bool sprite_database_file_strings_push_back_check(std::vector<string_hash>& vec, std::string& str);
|
|
|
|
const spr_db_spr_set spr_db_spr_set_null;
|
|
const spr_db_spr spr_db_spr_null;
|
|
|
|
spr_db_spr_file::spr_db_spr_file() : index(), texture() {
|
|
id = -1;
|
|
}
|
|
|
|
spr_db_spr_file::~spr_db_spr_file() {
|
|
|
|
}
|
|
|
|
spr_db_spr::spr_db_spr() : load_count() {
|
|
id = -1;
|
|
name_hash = hash_murmurhash_empty;
|
|
}
|
|
|
|
spr_db_spr::~spr_db_spr() {
|
|
|
|
}
|
|
|
|
spr_db_spr_set_file::spr_db_spr_set_file() {
|
|
id = -1;
|
|
index = -1;
|
|
}
|
|
|
|
spr_db_spr_set_file::~spr_db_spr_set_file() {
|
|
|
|
}
|
|
|
|
spr_db_spr_set::spr_db_spr_set() {
|
|
id = -1;
|
|
name_hash = hash_murmurhash_empty;
|
|
file_name_hash = hash_murmurhash_empty;
|
|
index = -1;
|
|
}
|
|
|
|
spr_db_spr_set::~spr_db_spr_set() {
|
|
|
|
}
|
|
|
|
sprite_database_file::sprite_database_file() : ready(), modern(), big_endian(), is_x() {
|
|
|
|
}
|
|
|
|
sprite_database_file::~sprite_database_file() {
|
|
|
|
}
|
|
|
|
void sprite_database_file::read(const char* path, bool modern) {
|
|
if (!path)
|
|
return;
|
|
|
|
if (!modern) {
|
|
char* path_bin = str_utils_add(path, ".bin");
|
|
if (path_check_file_exists(path_bin)) {
|
|
file_stream s;
|
|
s.open(path_bin, "rb");
|
|
if (s.check_not_null()) {
|
|
uint8_t* data = force_malloc_s(uint8_t, s.length);
|
|
s.read(data, s.length);
|
|
memory_stream s_bin;
|
|
s_bin.open(data, s.length);
|
|
sprite_database_file_classic_read_inner(this, s_bin);
|
|
free_def(data);
|
|
}
|
|
}
|
|
free_def(path_bin);
|
|
}
|
|
else {
|
|
char* path_spi = str_utils_add(path, ".spi");
|
|
if (path_check_file_exists(path_spi)) {
|
|
f2_struct st;
|
|
st.read(path_spi);
|
|
if (st.header.signature == reverse_endianness_uint32_t('SPDB')) {
|
|
memory_stream s_spdb;
|
|
s_spdb.open(st.data);
|
|
s_spdb.big_endian = st.header.use_big_endian;
|
|
sprite_database_file_modern_read_inner(this, s_spdb, st.header.length);
|
|
}
|
|
}
|
|
free_def(path_spi);
|
|
}
|
|
}
|
|
|
|
void sprite_database_file::read(const wchar_t* path, bool modern) {
|
|
if (!path)
|
|
return;
|
|
|
|
if (!modern) {
|
|
wchar_t* path_bin = str_utils_add(path, L".bin");
|
|
if (path_check_file_exists(path_bin)) {
|
|
file_stream s;
|
|
s.open(path_bin, L"rb");
|
|
if (s.check_not_null()) {
|
|
uint8_t* data = force_malloc_s(uint8_t, s.length);
|
|
s.read(data, s.length);
|
|
memory_stream s_bin;
|
|
s_bin.open(data, s.length);
|
|
sprite_database_file_classic_read_inner(this, s_bin);
|
|
free_def(data);
|
|
}
|
|
}
|
|
free_def(path_bin);
|
|
}
|
|
else {
|
|
wchar_t* path_spi = str_utils_add(path, L".spi");
|
|
if (path_check_file_exists(path_spi)) {
|
|
f2_struct st;
|
|
st.read(path_spi);
|
|
if (st.header.signature == reverse_endianness_uint32_t('SPDB')) {
|
|
memory_stream s_spdb;
|
|
s_spdb.open(st.data);
|
|
s_spdb.big_endian = st.header.use_big_endian;
|
|
sprite_database_file_modern_read_inner(this, s_spdb, st.header.length);
|
|
}
|
|
}
|
|
free_def(path_spi);
|
|
}
|
|
}
|
|
|
|
void sprite_database_file::read(const void* data, size_t size, bool modern) {
|
|
if (!data || !size)
|
|
return;
|
|
|
|
if (!modern) {
|
|
memory_stream s;
|
|
s.open(data, size);
|
|
sprite_database_file_classic_read_inner(this, s);
|
|
}
|
|
else {
|
|
f2_struct st;
|
|
st.read(data, size);
|
|
if (st.header.signature == reverse_endianness_uint32_t('SPDB')) {
|
|
memory_stream s_spdb;
|
|
s_spdb.open(st.data);
|
|
s_spdb.big_endian = st.header.use_big_endian;
|
|
sprite_database_file_modern_read_inner(this, s_spdb, st.header.length);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sprite_database_file::write(const char* path) {
|
|
if (!path || !ready)
|
|
return;
|
|
|
|
if (!modern) {
|
|
char* path_bin = str_utils_add(path, ".bin");
|
|
file_stream s;
|
|
s.open(path_bin, "wb");
|
|
if (s.check_not_null())
|
|
sprite_database_file_classic_write_inner(this, s);
|
|
free_def(path_bin);
|
|
}
|
|
else {
|
|
char* path_spi = str_utils_add(path, ".spi");
|
|
file_stream s;
|
|
s.open(path_spi, "wb");
|
|
if (s.check_not_null())
|
|
sprite_database_file_modern_write_inner(this, s);
|
|
free_def(path_spi);
|
|
}
|
|
}
|
|
|
|
void sprite_database_file::write(const wchar_t* path) {
|
|
if (!path || !ready)
|
|
return;
|
|
|
|
if (!modern) {
|
|
wchar_t* path_bin = str_utils_add(path, L".bin");
|
|
file_stream s;
|
|
s.open(path_bin, L"wb");
|
|
if (s.check_not_null())
|
|
sprite_database_file_classic_write_inner(this, s);
|
|
free_def(path_bin);
|
|
}
|
|
else {
|
|
wchar_t* path_spi = str_utils_add(path, L".spi");
|
|
file_stream s;
|
|
s.open(path_spi, L"wb");
|
|
if (s.check_not_null())
|
|
sprite_database_file_modern_write_inner(this, s);
|
|
free_def(path_spi);
|
|
}
|
|
}
|
|
|
|
void sprite_database_file::write(void** data, size_t* size) {
|
|
if (!data || !size || !ready)
|
|
return;
|
|
|
|
memory_stream s;
|
|
s.open();
|
|
if (!modern)
|
|
sprite_database_file_classic_write_inner(this, s);
|
|
else
|
|
sprite_database_file_modern_write_inner(this, s);
|
|
s.align_write(0x10);
|
|
s.copy(data, size);
|
|
}
|
|
|
|
bool sprite_database_file::load_file(void* data, const char* path, const char* file, uint32_t hash) {
|
|
size_t file_len = utf8_length(file);
|
|
|
|
const char* t = strrchr(file, '.');
|
|
if (t)
|
|
file_len = t - file;
|
|
|
|
std::string s;
|
|
s.assign(path);
|
|
s.append(file, file_len);
|
|
|
|
sprite_database_file* spr_db = (sprite_database_file*)data;
|
|
spr_db->read(s.c_str(), spr_db->modern);
|
|
|
|
return spr_db->ready;
|
|
}
|
|
|
|
sprite_database::sprite_database() {
|
|
|
|
}
|
|
|
|
sprite_database::~sprite_database() {
|
|
|
|
}
|
|
|
|
void sprite_database::add(sprite_database_file* spr_db_file) {
|
|
if (!spr_db_file || !spr_db_file->ready)
|
|
return;
|
|
|
|
size_t sprite_sets_count = this->spr_set_ids.size();
|
|
|
|
spr_set_ids.reserve(spr_db_file->sprite_set.size());
|
|
spr_set_indices.reserve(spr_db_file->sprite_set.size());
|
|
spr_set_names.reserve(spr_db_file->sprite_set.size());
|
|
|
|
for (spr_db_spr_set_file& i : spr_db_file->sprite_set) {
|
|
spr_sets.push_back({});
|
|
spr_db_spr_set& spr_set = spr_sets.back();
|
|
spr_set.id = i.id;
|
|
spr_set.name.assign(i.name);
|
|
spr_set.name_hash = hash_string_murmurhash(spr_set.name);
|
|
spr_set.file_name.assign(i.file_name);
|
|
spr_set.file_name_hash = hash_string_murmurhash(spr_set.file_name);
|
|
spr_set.index = (uint32_t)(sprite_sets_count + i.index);
|
|
|
|
spr_set_ids.push_back({ spr_set.id, &spr_set });
|
|
spr_set_indices.push_back({ spr_set.index, &spr_set });
|
|
spr_set_names.push_back({ spr_set.name, &spr_set });
|
|
|
|
spr_ids.reserve(i.sprite.size());
|
|
spr_names.reserve(i.sprite.size());
|
|
spr_indices.reserve(i.sprite.size());
|
|
|
|
for (spr_db_spr_file& j : i.sprite) {
|
|
sprs.push_back({});
|
|
spr_db_spr& spr = sprs.back();
|
|
spr.id = j.id;
|
|
spr.name.assign(j.name);
|
|
spr.name_hash = hash_string_murmurhash(spr.name);
|
|
//spr.info = { j.index, (uint16_t)((j.texture ? 0x1000 : 0x0000) | spr_set.index) };
|
|
spr.info = { j.index, (uint16_t)((j.texture ? 0x4000 : 0x0000) | spr_set.index) };
|
|
spr.load_count = 1;
|
|
|
|
spr_ids.push_back({ spr.id, &spr });
|
|
spr_names.push_back({ spr.name, &spr });
|
|
spr_indices.push_back({ spr.info, &spr });
|
|
}
|
|
}
|
|
|
|
spr_set_ids.combine();
|
|
spr_set_indices.combine();
|
|
spr_set_names.combine();
|
|
spr_ids.combine();
|
|
spr_names.combine();
|
|
spr_indices.combine();
|
|
}
|
|
|
|
void sprite_database::clear() {
|
|
spr_sets.clear();
|
|
spr_set_ids.clear();
|
|
spr_set_indices.clear();
|
|
spr_set_names.clear();
|
|
sprs.clear();
|
|
spr_ids.clear();
|
|
spr_names.clear();
|
|
spr_indices.clear();
|
|
}
|
|
|
|
void sprite_database::add_spr_set(uint32_t set_id, uint32_t index) {
|
|
if (set_id == -1 || set_id == hash_murmurhash_empty)
|
|
return;
|
|
|
|
spr_sets.push_back({});
|
|
spr_db_spr_set& set = spr_sets.back();
|
|
set.id = set_id;
|
|
set.index = index;
|
|
|
|
spr_set_indices.push_back({ set.index, &set });
|
|
spr_set_ids.push_back({ set.id, &set });
|
|
|
|
spr_set_indices.combine();
|
|
spr_set_ids.combine();
|
|
}
|
|
|
|
void sprite_database::parse(const spr_db_spr_set_file* set_file,
|
|
std::string& set_name, std::vector<uint32_t>& sprite_ids) {
|
|
if (!set_file)
|
|
return;
|
|
|
|
auto elem = spr_set_ids.find(set_file->id);
|
|
if (elem == spr_set_ids.end())
|
|
return;
|
|
|
|
uint16_t set_index = (uint16_t)elem->second->index;
|
|
|
|
spr_set_names.push_back({ set_file->name, elem->second });
|
|
|
|
sprite_ids.reserve(set_file->sprite.size());
|
|
for (const spr_db_spr_file& i : set_file->sprite) {
|
|
uint32_t id = i.id;
|
|
sprite_ids.push_back(id);
|
|
|
|
auto j_begin = sprs.begin();
|
|
auto j_end = sprs.end();
|
|
auto j = j_begin;
|
|
while (j != j_end)
|
|
if (j->id == id)
|
|
break;
|
|
else
|
|
j++;
|
|
|
|
spr_db_spr* spr;
|
|
if (j == j_end) {
|
|
sprs.push_back({});
|
|
spr = &sprs.back();
|
|
}
|
|
else
|
|
spr = &*j;
|
|
|
|
spr->id = id;
|
|
spr->name.assign(i.name);
|
|
spr->name_hash = hash_string_murmurhash(spr->name);
|
|
//spr->info = { i.index, (uint16_t)((i.texture ? 0x1000 : 0x0000) | set_index) };
|
|
spr->info = { i.index, (uint16_t)((i.texture ? 0x4000 : 0x0000) | set_index) };
|
|
spr->load_count++;
|
|
|
|
spr_ids.push_back({ spr->id, spr });
|
|
spr_names.push_back({ spr->name, spr });
|
|
spr_indices.push_back({ spr->info, spr });
|
|
}
|
|
|
|
spr_set_names.combine();
|
|
spr_ids.combine();
|
|
spr_names.combine();
|
|
spr_indices.combine();
|
|
}
|
|
|
|
void sprite_database::remove_spr_set(uint32_t set_id, uint32_t index,
|
|
const char* set_name, std::vector<uint32_t>& sprite_ids) {
|
|
if (set_name && *set_name) {
|
|
auto elem = spr_set_names.find(set_name);
|
|
if (elem != spr_set_names.end())
|
|
spr_set_names.erase(elem);
|
|
}
|
|
|
|
if (index != -1) {
|
|
auto elem = spr_set_indices.find(index);
|
|
if (elem != spr_set_indices.end())
|
|
spr_set_indices.erase(elem);
|
|
}
|
|
|
|
if (set_id != -1 && set_id != hash_murmurhash_empty) {
|
|
auto elem = spr_set_ids.find(set_id);
|
|
if (elem != spr_set_ids.end())
|
|
spr_set_ids.erase(elem);
|
|
|
|
auto i_begin = spr_sets.begin();
|
|
auto i_end = spr_sets.end();
|
|
auto i = i_begin;
|
|
while (i != i_end)
|
|
if (i->id == set_id)
|
|
i = spr_sets.erase(i);
|
|
else
|
|
i++;
|
|
}
|
|
|
|
for (auto& i : sprite_ids) {
|
|
auto j_begin = sprs.begin();
|
|
auto j_end = sprs.end();
|
|
auto j = j_begin;
|
|
while (j != j_end) {
|
|
if (j->id != i || --j->load_count > 0) {
|
|
j++;
|
|
continue;
|
|
}
|
|
|
|
auto elem_id = spr_ids.find(j->id);
|
|
if (elem_id != spr_ids.end())
|
|
spr_ids.erase(elem_id);
|
|
|
|
auto elem_index = spr_indices.find(j->info);
|
|
if (elem_index != spr_indices.end())
|
|
spr_indices.erase(elem_index);
|
|
|
|
auto elem_name = spr_names.find(j->name);
|
|
if (elem_name != spr_names.end())
|
|
spr_names.erase(elem_name);
|
|
|
|
j = sprs.erase(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
const spr_db_spr_set* sprite_database::get_spr_set_by_name(const char* name) const {
|
|
auto elem = spr_set_names.find(std::string(name));
|
|
if (elem != spr_set_names.end())
|
|
return elem->second;
|
|
return &spr_db_spr_set_null;
|
|
}
|
|
|
|
const spr_db_spr_set* sprite_database::get_spr_set_by_id(uint32_t set_id) const {
|
|
auto elem = spr_set_ids.find(set_id);
|
|
if (elem != spr_set_ids.end())
|
|
return elem->second;
|
|
return &spr_db_spr_set_null;
|
|
}
|
|
|
|
const spr_db_spr_set* sprite_database::get_spr_set_by_index(uint32_t index) const {
|
|
auto elem = spr_set_indices.find(index);
|
|
if (elem != spr_set_indices.end())
|
|
return elem->second;
|
|
return &spr_db_spr_set_null;
|
|
}
|
|
|
|
const spr_db_spr* sprite_database::get_spr_by_name(const char* name) const {
|
|
auto elem = spr_names.find(std::string(name));
|
|
if (elem != spr_names.end())
|
|
return elem->second;
|
|
return &spr_db_spr_null;
|
|
}
|
|
|
|
const spr_db_spr* sprite_database::get_spr_by_id(uint32_t id) const {
|
|
auto elem = spr_ids.find(id);
|
|
if (elem != spr_ids.end())
|
|
return elem->second;
|
|
return &spr_db_spr_null;
|
|
}
|
|
|
|
const spr_db_spr* sprite_database::get_spr_by_set_id_index(uint32_t set_id, uint32_t index) const {
|
|
auto elem = spr_indices.find({ (uint16_t)index, (uint16_t)get_spr_set_by_id(set_id)->index });
|
|
if (elem != spr_indices.end())
|
|
return elem->second;
|
|
return &spr_db_spr_null;
|
|
}
|
|
|
|
const spr_db_spr* sprite_database::get_tex_by_set_id_index(uint32_t set_id, uint32_t index) const {
|
|
//auto elem = spr_indices.find({ (uint16_t)index, (uint16_t)(0x1000 | get_spr_set_by_id(set_id)->index) });
|
|
auto elem = spr_indices.find({ (uint16_t)index, (uint16_t)(0x4000 | get_spr_set_by_id(set_id)->index) });
|
|
if (elem != spr_indices.end())
|
|
return elem->second;
|
|
return &spr_db_spr_null;
|
|
}
|
|
|
|
const char* sprite_database::get_spr_set_file_name(uint32_t set_id) const {
|
|
return get_spr_set_by_id(set_id)->file_name.c_str();
|
|
}
|
|
|
|
uint32_t sprite_database::get_spr_set_id_by_name(const char* name) const {
|
|
return get_spr_set_by_name(name)->id;
|
|
}
|
|
|
|
uint32_t sprite_database::get_spr_set_id_by_name_index(uint32_t index) const {
|
|
return spr_set_names[index].second->id;
|
|
}
|
|
|
|
const char* sprite_database::get_spr_set_name(uint32_t set_id) const {
|
|
return get_spr_set_by_id(set_id)->name.c_str();
|
|
}
|
|
|
|
static void sprite_database_file_classic_read_inner(sprite_database_file* spr_db, stream& s) {
|
|
uint32_t sprite_sets_count = s.read_uint32_t();
|
|
uint32_t sprite_sets_offset = s.read_uint32_t();
|
|
uint32_t sprites_count = s.read_uint32_t();
|
|
uint32_t sprites_offset = s.read_uint32_t();
|
|
|
|
spr_db->sprite_set.resize(sprite_sets_count);
|
|
|
|
spr_db_spr_set_file* spr_db_spr_set = spr_db->sprite_set.data();
|
|
|
|
s.position_push(sprite_sets_offset, SEEK_SET);
|
|
for (uint32_t i = 0; i < sprite_sets_count; i++) {
|
|
spr_db_spr_set_file& spr_set = spr_db_spr_set[i];
|
|
spr_set.id = s.read_uint32_t();
|
|
spr_set.name = s.read_string_null_terminated_offset(s.read_uint32_t());
|
|
spr_set.file_name = s.read_string_null_terminated_offset(s.read_uint32_t());
|
|
spr_set.index = s.read_uint32_t();
|
|
}
|
|
s.position_pop();
|
|
|
|
s.position_push(sprites_offset, SEEK_SET);
|
|
for (uint32_t i = 0; i < sprites_count; i++) {
|
|
uint32_t id = s.read_uint32_t();
|
|
uint32_t name_offset = s.read_uint32_t();
|
|
uint32_t info = s.read_uint32_t();
|
|
|
|
uint16_t index = (uint16_t)(info & 0xFFFF);
|
|
uint16_t set_index = (uint16_t)((info >> 16) & 0x0FFF);
|
|
bool texture = !!((info >> 16) & 0x1000);
|
|
|
|
spr_db_spr_set_file& spr_set = spr_db_spr_set[set_index];
|
|
|
|
spr_set.sprite.push_back({});
|
|
spr_db_spr_file& spr = spr_set.sprite.back();
|
|
spr.id = id;
|
|
spr.name.assign(s.read_string_null_terminated_offset(name_offset));
|
|
spr.index = index;
|
|
spr.texture = texture;
|
|
}
|
|
s.position_pop();
|
|
|
|
spr_db->ready = true;
|
|
spr_db->modern = false;
|
|
spr_db->big_endian = false;
|
|
spr_db->is_x = false;
|
|
}
|
|
|
|
static void sprite_database_file_classic_write_inner(sprite_database_file* spr_db, stream& s) {
|
|
s.write_uint32_t(0x90669066);
|
|
s.write_uint32_t(0x90669066);
|
|
s.write_uint32_t(0x90669066);
|
|
s.write_uint32_t(0x90669066);
|
|
s.write_uint32_t(0x90669066);
|
|
s.write_uint32_t(0x90669066);
|
|
s.write_uint32_t(0x90669066);
|
|
s.write_uint32_t(0x90669066);
|
|
s.align_write(0x20);
|
|
|
|
size_t sprite_sets_count = spr_db->sprite_set.size();
|
|
|
|
size_t sprites_count = 0;
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set)
|
|
sprites_count += i.sprite.size();
|
|
|
|
int64_t sprites_offset = s.get_position();
|
|
s.write(0x0C * sprites_count);
|
|
s.align_write(0x20);
|
|
|
|
int64_t sprite_sets_offset = s.get_position();
|
|
s.write(0x10 * spr_db->sprite_set.size());
|
|
s.align_write(0x20);
|
|
|
|
std::vector<string_hash> strings;
|
|
std::vector<int64_t> string_offsets;
|
|
|
|
strings.reserve(sprite_sets_count + sprites_count);
|
|
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
for (spr_db_spr_file& j : i.sprite)
|
|
if (j.texture && sprite_database_file_strings_push_back_check(strings, j.name)) {
|
|
string_offsets.push_back(s.get_position());
|
|
s.write_string_null_terminated(j.name);
|
|
}
|
|
|
|
for (spr_db_spr_file& j : i.sprite)
|
|
if (!j.texture && sprite_database_file_strings_push_back_check(strings, j.name)) {
|
|
string_offsets.push_back(s.get_position());
|
|
s.write_string_null_terminated(j.name);
|
|
}
|
|
}
|
|
s.align_write(0x04);
|
|
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
if (sprite_database_file_strings_push_back_check(strings, i.name)) {
|
|
string_offsets.push_back(s.get_position());
|
|
s.write_string_null_terminated(i.name);
|
|
}
|
|
|
|
if (sprite_database_file_strings_push_back_check(strings, i.file_name)) {
|
|
string_offsets.push_back(s.get_position());
|
|
s.write_string_null_terminated(i.file_name);
|
|
}
|
|
}
|
|
s.align_write(0x04);
|
|
|
|
s.position_push(sprites_offset, SEEK_SET);
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
uint16_t sprite_set_index = (uint16_t)(i.index & 0x0FFF);
|
|
|
|
for (spr_db_spr_file& j : i.sprite) {
|
|
if (j.texture)
|
|
continue;
|
|
|
|
s.write_uint32_t(j.id);
|
|
s.write_uint32_t((uint32_t)sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, j.name));
|
|
s.write_uint32_t(((0x1000 | sprite_set_index) << 16) | j.index);
|
|
}
|
|
|
|
for (spr_db_spr_file& j : i.sprite) {
|
|
if (!j.texture)
|
|
continue;
|
|
|
|
s.write_uint32_t(j.id);
|
|
s.write_uint32_t((uint32_t)sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, j.name));
|
|
s.write_uint32_t((sprite_set_index << 16) | j.index);
|
|
}
|
|
}
|
|
s.position_pop();
|
|
|
|
s.position_push(sprite_sets_offset, SEEK_SET);
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
s.write_uint32_t(i.id);
|
|
s.write_uint32_t((uint32_t)sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, i.name));
|
|
s.write_uint32_t((uint32_t)sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, i.file_name));
|
|
s.write_uint32_t(i.index);
|
|
}
|
|
s.position_pop();
|
|
|
|
s.position_push(0x00, SEEK_SET);
|
|
s.write_uint32_t((uint32_t)sprite_sets_count);
|
|
s.write_uint32_t((uint32_t)sprite_sets_offset);
|
|
s.write_uint32_t((uint32_t)sprites_count);
|
|
s.write_uint32_t((uint32_t)sprites_offset);
|
|
s.position_pop();
|
|
}
|
|
|
|
static void sprite_database_file_modern_read_inner(sprite_database_file* spr_db, stream& s, uint32_t header_length) {
|
|
bool big_endian = s.big_endian;
|
|
bool is_x = true;
|
|
|
|
s.set_position(0x04, SEEK_SET);
|
|
is_x &= s.read_uint32_t_reverse_endianness() == 0;
|
|
|
|
s.set_position(0x00, SEEK_SET);
|
|
|
|
uint32_t sprite_sets_count = s.read_uint32_t_reverse_endianness();
|
|
int64_t sprite_sets_offset = s.read_offset(header_length, is_x);
|
|
uint32_t sprites_count = s.read_uint32_t_reverse_endianness();
|
|
int64_t sprites_offset = s.read_offset(header_length, is_x);
|
|
|
|
spr_db->sprite_set.resize(sprite_sets_count);
|
|
|
|
spr_db_spr_set_file* spr_db_spr_set = spr_db->sprite_set.data();
|
|
|
|
s.position_push(sprite_sets_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (uint32_t i = 0; i < sprite_sets_count; i++) {
|
|
spr_db_spr_set_file& spr_set = spr_db_spr_set[i];
|
|
spr_set.id = s.read_uint32_t_reverse_endianness();
|
|
spr_set.name = s.read_string_null_terminated_offset(s.read_offset_f2(header_length));
|
|
spr_set.file_name = s.read_string_null_terminated_offset(s.read_offset_f2(header_length));
|
|
spr_set.index = s.read_uint32_t_reverse_endianness();
|
|
}
|
|
else
|
|
for (uint32_t i = 0; i < sprite_sets_count; i++) {
|
|
spr_db_spr_set_file& spr_set = spr_db_spr_set[i];
|
|
spr_set.id = s.read_uint32_t_reverse_endianness();
|
|
spr_set.name = s.read_string_null_terminated_offset(s.read_offset_x());
|
|
spr_set.file_name = s.read_string_null_terminated_offset(s.read_offset_x());
|
|
spr_set.index = s.read_uint32_t_reverse_endianness();
|
|
s.align_read(0x08);
|
|
}
|
|
s.position_pop();
|
|
|
|
s.position_push(sprites_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (uint32_t i = 0; i < sprites_count; i++) {
|
|
uint32_t id = s.read_uint32_t_reverse_endianness();
|
|
int64_t name_offset = s.read_offset_f2(header_length);
|
|
uint32_t info = s.read_uint32_t_reverse_endianness();
|
|
|
|
uint16_t index = (uint16_t)(info & 0xFFFF);
|
|
uint16_t set_index = (uint16_t)((info >> 16) & 0x0FFF);
|
|
bool texture = !!((info >> 16) & 0x1000);
|
|
|
|
spr_db_spr_set_file& spr_set = spr_db_spr_set[set_index];
|
|
|
|
spr_set.sprite.push_back({});
|
|
spr_db_spr_file& spr = spr_set.sprite.back();
|
|
spr.id = id;
|
|
spr.name.assign(s.read_string_null_terminated_offset(name_offset));
|
|
spr.index = index;
|
|
spr.texture = texture;
|
|
}
|
|
else
|
|
for (uint32_t i = 0; i < sprites_count; i++) {
|
|
uint32_t id = s.read_uint32_t_reverse_endianness();
|
|
int64_t name_offset = s.read_offset_x();
|
|
uint32_t info = s.read_uint32_t_reverse_endianness();
|
|
s.align_read(0x08);
|
|
|
|
uint16_t index = (uint16_t)(info & 0xFFFF);
|
|
uint16_t set_index = (uint16_t)((info >> 16) & 0x0FFF);
|
|
bool texture = !!((info >> 16) & 0x1000);
|
|
|
|
spr_db_spr_set_file& spr_set = spr_db_spr_set[set_index];
|
|
|
|
spr_set.sprite.push_back({});
|
|
spr_db_spr_file& spr = spr_set.sprite.back();
|
|
spr.id = id;
|
|
spr.name.assign(s.read_string_null_terminated_offset(name_offset));
|
|
spr.index = index;
|
|
spr.texture = texture;
|
|
}
|
|
s.position_pop();
|
|
|
|
spr_db->ready = true;
|
|
spr_db->modern = true;
|
|
spr_db->big_endian = big_endian;
|
|
spr_db->is_x = is_x;
|
|
}
|
|
|
|
static void sprite_database_file_modern_write_inner(sprite_database_file* spr_db, stream& s) {
|
|
bool big_endian = spr_db->big_endian;
|
|
bool is_x = spr_db->is_x;
|
|
|
|
memory_stream s_spdb;
|
|
s_spdb.open();
|
|
s_spdb.big_endian = big_endian;
|
|
|
|
uint32_t off;
|
|
enrs e;
|
|
enrs_entry ee;
|
|
pof pof;
|
|
|
|
size_t sprite_sets_count = spr_db->sprite_set.size();
|
|
|
|
size_t sprites_count = 0;
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set)
|
|
sprites_count += i.sprite.size();
|
|
|
|
if (!is_x) {
|
|
ee = { 0, 1, 16, 1 };
|
|
ee.append(0, 4, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = 16;
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 1, 16, (uint32_t)sprite_sets_count };
|
|
ee.append(0, 4, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sprite_sets_count * 16ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 1, 12, (uint32_t)sprites_count };
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sprites_count * 12ULL);
|
|
off = align_val(off, 0x10);
|
|
}
|
|
else {
|
|
ee = { 0, 4, 32, 1 };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = 32;
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 3, 32, (uint32_t)sprite_sets_count };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 2, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sprite_sets_count * 32ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 3, 24, (uint32_t)sprites_count };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sprites_count * 24ULL);
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
s_spdb.write(is_x ? 0x20 : 0x10);
|
|
s_spdb.align_write(0x10);
|
|
|
|
int64_t sprite_sets_offset = s_spdb.get_position();
|
|
s_spdb.write(sprite_sets_count * (is_x ? 0x20ULL : 0x10ULL));
|
|
s_spdb.align_write(0x10);
|
|
|
|
int64_t sprites_offset = s_spdb.get_position();
|
|
s_spdb.write(sprites_count * (is_x ? 0x18ULL : 0x0CULL));
|
|
s_spdb.align_write(0x10);
|
|
|
|
std::vector<string_hash> strings;
|
|
std::vector<int64_t> string_offsets;
|
|
|
|
strings.reserve(sprite_sets_count + sprites_count);
|
|
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
if (sprite_database_file_strings_push_back_check(strings, i.name)) {
|
|
string_offsets.push_back(s_spdb.get_position());
|
|
s_spdb.write_string_null_terminated(i.name);
|
|
}
|
|
|
|
if (sprite_database_file_strings_push_back_check(strings, i.file_name)) {
|
|
string_offsets.push_back(s_spdb.get_position());
|
|
s_spdb.write_string_null_terminated(i.file_name);
|
|
}
|
|
}
|
|
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
for (spr_db_spr_file& j : i.sprite)
|
|
if (j.texture && sprite_database_file_strings_push_back_check(strings, j.name)) {
|
|
string_offsets.push_back(s_spdb.get_position());
|
|
s_spdb.write_string_null_terminated(j.name);
|
|
}
|
|
|
|
for (spr_db_spr_file& j : i.sprite)
|
|
if (!j.texture && sprite_database_file_strings_push_back_check(strings, j.name)) {
|
|
string_offsets.push_back(s_spdb.get_position());
|
|
s_spdb.write_string_null_terminated(j.name);
|
|
}
|
|
}
|
|
s_spdb.align_write(0x10);
|
|
|
|
s_spdb.position_push(0x00, SEEK_SET);
|
|
s_spdb.write_uint32_t_reverse_endianness((uint32_t)sprite_sets_count);
|
|
io_write_offset_pof_add(s_spdb, sprite_sets_offset, 0x20, is_x, &pof);
|
|
s_spdb.write_uint32_t_reverse_endianness((uint32_t)sprites_count);
|
|
io_write_offset_pof_add(s_spdb, sprites_offset, 0x20, is_x, &pof);
|
|
s_spdb.position_pop();
|
|
|
|
s_spdb.position_push(sprite_sets_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
s_spdb.write_uint32_t_reverse_endianness(i.id);
|
|
io_write_offset_f2_pof_add(s_spdb, sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, i.name), 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s_spdb, sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, i.file_name), 0x20, &pof);
|
|
s_spdb.write_uint32_t_reverse_endianness(i.index);
|
|
}
|
|
else
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
s_spdb.write_uint32_t_reverse_endianness(i.id);
|
|
io_write_offset_x_pof_add(s_spdb, sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, i.name), &pof);
|
|
io_write_offset_x_pof_add(s_spdb, sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, i.file_name), &pof);
|
|
s_spdb.write_uint32_t_reverse_endianness(i.index);
|
|
}
|
|
s_spdb.position_pop();
|
|
|
|
s_spdb.position_push(sprites_offset, SEEK_SET);
|
|
for (spr_db_spr_set_file& i : spr_db->sprite_set) {
|
|
uint16_t sprite_set_index = (uint16_t)(i.index & 0x0FFF);
|
|
|
|
if (!is_x) {
|
|
for (spr_db_spr_file& j : i.sprite) {
|
|
if (!j.texture)
|
|
continue;
|
|
|
|
s_spdb.write_uint32_t_reverse_endianness(j.id);
|
|
io_write_offset_f2_pof_add(s_spdb, sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, j.name), 0x20, &pof);
|
|
s_spdb.write_uint32_t_reverse_endianness(((0x1000 | sprite_set_index) << 16) | j.index);
|
|
}
|
|
|
|
for (spr_db_spr_file& j : i.sprite) {
|
|
if (j.texture)
|
|
continue;
|
|
|
|
s_spdb.write_uint32_t_reverse_endianness(j.id);
|
|
io_write_offset_f2_pof_add(s_spdb, sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, j.name), 0x20, &pof);
|
|
s_spdb.write_uint32_t_reverse_endianness((sprite_set_index << 16) | j.index);
|
|
}
|
|
}
|
|
else {
|
|
for (spr_db_spr_file& j : i.sprite) {
|
|
if (!j.texture)
|
|
continue;
|
|
|
|
s_spdb.write_uint32_t_reverse_endianness(j.id);
|
|
io_write_offset_x_pof_add(s_spdb, sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, j.name), &pof);
|
|
s_spdb.write_uint32_t_reverse_endianness(((0x1000 | sprite_set_index) << 16) | j.index);
|
|
s_spdb.align_write(0x08);
|
|
}
|
|
|
|
for (spr_db_spr_file& j : i.sprite) {
|
|
if (j.texture)
|
|
continue;
|
|
|
|
s_spdb.write_uint32_t_reverse_endianness(j.id);
|
|
io_write_offset_x_pof_add(s_spdb, sprite_database_file_strings_get_string_offset(strings,
|
|
string_offsets, j.name), &pof);
|
|
s_spdb.write_uint32_t_reverse_endianness((sprite_set_index << 16) | j.index);
|
|
s_spdb.align_write(0x08);
|
|
}
|
|
}
|
|
}
|
|
s_spdb.position_pop();
|
|
|
|
f2_struct st;
|
|
s_spdb.align_write(0x10);
|
|
s_spdb.copy(st.data);
|
|
s_spdb.close();
|
|
|
|
st.enrs = e;
|
|
st.pof = pof;
|
|
|
|
st.header.signature = reverse_endianness_uint32_t('SPDB');
|
|
st.header.length = 0x20;
|
|
st.header.use_big_endian = big_endian;
|
|
st.header.use_section_size = true;
|
|
|
|
st.write(s, true, spr_db->is_x);
|
|
}
|
|
|
|
inline static int64_t sprite_database_file_strings_get_string_offset(std::vector<string_hash>& vec,
|
|
std::vector<int64_t>& vec_off, std::string& str) {
|
|
uint64_t hash_fnv1a64m = hash_string_fnv1a64m(str);
|
|
uint64_t hash_murmurhash = hash_string_murmurhash(str);
|
|
for (string_hash& i : vec)
|
|
if (hash_fnv1a64m == i.hash_fnv1a64m && hash_murmurhash == i.hash_murmurhash)
|
|
return vec_off[&i - vec.data()];
|
|
return 0;
|
|
}
|
|
|
|
inline static bool sprite_database_file_strings_push_back_check(std::vector<string_hash>& vec, std::string& str) {
|
|
uint64_t hash_fnv1a64m = hash_string_fnv1a64m(str);
|
|
uint64_t hash_murmurhash = hash_string_murmurhash(str);
|
|
for (string_hash& i : vec)
|
|
if (hash_fnv1a64m == i.hash_fnv1a64m && hash_murmurhash == i.hash_murmurhash)
|
|
return false;
|
|
|
|
vec.push_back(str);
|
|
return true;
|
|
}
|