support uncompressed episode 3 maps

This commit is contained in:
Martin Michelsen
2022-06-03 00:25:01 -07:00
parent 7efa6374ea
commit e139745f51
34 changed files with 70 additions and 33 deletions
+9 -4
View File
@@ -96,10 +96,11 @@ struct prs_compress_ctx {
}
};
string prs_compress(const string& data) {
string prs_compress(const void* vdata, size_t size) {
const uint8_t* data = reinterpret_cast<const uint8_t*>(vdata);
prs_compress_ctx pc;
ssize_t data_ssize = static_cast<ssize_t>(data.size());
ssize_t data_ssize = static_cast<ssize_t>(size);
ssize_t read_offset = 0;
while (read_offset < data_ssize) {
@@ -117,8 +118,8 @@ string prs_compress(const string& data) {
while ((this_size < 0x100) && // max copy size is 255 bytes
((this_offset + this_size) < 0) && // don't copy past the read offset
(this_size <= data_ssize - read_offset) && // don't copy past the end
!memcmp(data.data() + read_offset + this_offset,
data.data() + read_offset, this_size)) {
!memcmp(data + read_offset + this_offset, data + read_offset,
this_size)) {
this_size++;
}
this_size--;
@@ -143,6 +144,10 @@ string prs_compress(const string& data) {
return pc.finish();
}
string prs_compress(const string& data) {
return prs_compress(data.data(), data.size());
}
static int16_t get_u8_or_eof(StringReader& r) {
+2
View File
@@ -6,6 +6,8 @@
std::string prs_compress(const void* vdata, size_t size);
std::string prs_compress(const std::string& data);
std::string prs_decompress(const std::string& data, size_t max_size = 0);
size_t prs_decompress_size(const std::string& data, size_t max_size = 0);
+33 -18
View File
@@ -774,36 +774,51 @@ Ep3DataIndex::Ep3DataIndex(const string& directory) {
}
for (const auto& filename : list_directory(directory)) {
if (ends_with(filename, ".mnm")) {
try {
string compressed_data = load_file(directory + "/" + filename);
// There's a small header (Ep3CompressedMapHeader) before the compressed
// data, which we ignore
string data_to_decompress = compressed_data.substr(8);
string data = prs_decompress(data_to_decompress);
if (data.size() != sizeof(Ep3Map)) {
throw runtime_error(string_printf(
"decompressed data size is incorrect (expected %zu bytes, read %zu bytes)",
sizeof(Ep3Map), data.size()));
}
try {
shared_ptr<MapEntry> entry;
shared_ptr<MapEntry> entry(new MapEntry(
{*reinterpret_cast<const Ep3Map*>(data.data()), move(compressed_data)}));
if (ends_with(filename, ".mnmd")) {
entry.reset(new MapEntry(load_object_file<Ep3Map>(directory + "/" + filename)));
} else if (ends_with(filename, ".mnm")) {
entry.reset(new MapEntry(load_file(directory + "/" + filename)));
}
if (entry.get()) {
if (!this->maps.emplace(entry->map.map_number, entry).second) {
throw runtime_error("duplicate map number");
}
string name = entry->map.name;
log(INFO, "Indexed Episode 3 map %s (%08" PRIX32 "; %s)",
filename.c_str(), entry->map.map_number.load(), name.c_str());
} catch (const exception& e) {
log(WARNING, "Failed to index Episode 3 map %s: %s",
filename.c_str(), e.what());
}
} catch (const exception& e) {
log(WARNING, "Failed to index Episode 3 map %s: %s",
filename.c_str(), e.what());
}
}
}
Ep3DataIndex::MapEntry::MapEntry(const Ep3Map& map) : map(map) { }
Ep3DataIndex::MapEntry::MapEntry(const string& compressed)
: compressed_data(compressed) {
string decompressed = prs_decompress(this->compressed_data);
if (decompressed.size() != sizeof(Ep3Map)) {
throw runtime_error(string_printf(
"decompressed data size is incorrect (expected %zu bytes, read %zu bytes)",
sizeof(Ep3Map), decompressed.size()));
}
this->map = *reinterpret_cast<const Ep3Map*>(decompressed.data());
}
string Ep3DataIndex::MapEntry::compressed() const {
if (this->compressed_data.empty()) {
this->compressed_data = prs_compress(&this->map, sizeof(this->map));
}
return this->compressed_data;
}
const string& Ep3DataIndex::get_compressed_card_definitions() const {
if (this->compressed_card_definitions.empty()) {
throw runtime_error("card definitions are not available");
+12 -4
View File
@@ -295,7 +295,7 @@ struct Ep3Map { // .mnm format (after decompressing and discarding the header)
ptext<char, 0x10> name;
parray<be_uint16_t, 0x7E> unknown_a3;
} __attribute__((packed));
/* 20F0 */ parray<NPCCharacter, 3> npc_chars; // Unused if name[0] == 0
/* 20F0 */ NPCCharacter npc_chars[3]; // Unused if name[0] == 0
/* 242C */ parray<uint8_t, 0x14> unknown_a8; // Always FF?
/* 2440 */ ptext<char, 0x190> before_message;
/* 25D0 */ ptext<char, 0x190> after_message;
@@ -305,7 +305,7 @@ struct Ep3Map { // .mnm format (after decompressing and discarding the header)
be_uint16_t unknown_a2; // Always 0x0064 if valid, 0xFFFF if unused?
ptext<char, 0x40> strings[4];
} __attribute__((packed)); // Total size: 0x104 bytes
/* 28F0 */ parray<DialogueSet, 0x10> dialogue_sets[3]; // Up to 0x10 per valid NPC
/* 28F0 */ DialogueSet dialogue_sets[3][0x10]; // Up to 0x10 per valid NPC
/* 59B0 */ be_uint16_t reward_card_id; // TODO: This could be an array. The only examples I've seen have only one here
/* 59B2 */ parray<be_uint16_t, 0x33> unknown_a9;
/* 5A18 */
@@ -320,9 +320,17 @@ public:
std::vector<std::string> text;
};
struct MapEntry {
class MapEntry {
public:
Ep3Map map;
std::string compressed_data;
MapEntry(const Ep3Map& map);
MapEntry(const std::string& compressed_data);
std::string compressed() const;
private:
mutable std::string compressed_data;
};
const std::string& get_compressed_card_definitions() const;
+5 -4
View File
@@ -26,6 +26,7 @@
#include <resource_file/Emulators/PPC32Emulator.hh>
#endif
#include "Compression.hh"
#include "PSOProtocol.hh"
#include "SendCommands.hh"
#include "ReceiveCommands.hh"
@@ -637,11 +638,11 @@ static bool process_server_60_62_6C_6D_C9_CB(shared_ptr<ServerState>,
if ((session.version == GameVersion::GC) && (data.size() >= 0x14)) {
PSOSubcommand* subs = &check_size_t<PSOSubcommand>(data, 0x14, 0xFFFF);
if (subs[0].dword == 0x000000B6 && subs[2].dword == 0x00000041) {
string filename = string_printf("map%08" PRIX32 ".%" PRIu64 ".mnm",
string filename = string_printf("map%08" PRIX32 ".%" PRIu64 ".mnmd",
subs[3].dword.load(), now());
string file_data = data.substr(0x0C);
save_file(filename, file_data);
session.log(INFO, "Wrote %zu bytes to %s", file_data.size(), filename.c_str());
string map_data = prs_decompress(data.substr(0x14));
save_file(filename, map_data);
session.log(INFO, "Wrote %zu bytes to %s", map_data.size(), filename.c_str());
}
}
}
+9 -3
View File
@@ -1399,13 +1399,19 @@ void send_ep3_map_list(shared_ptr<ServerState> s, shared_ptr<Lobby> l) {
// sends the map data for the chosen map to all players in the game
void send_ep3_map_data(shared_ptr<ServerState> s, shared_ptr<Lobby> l, uint32_t map_id) {
auto entry = s->ep3_data_index->get_map(map_id);
const auto& compressed = entry->compressed();
string data(12, '\0');
string data(0x14, '\0');
PSOSubcommand* subs = reinterpret_cast<PSOSubcommand*>(data.data());
subs[0].dword = 0x000000B6;
subs[1].dword = (19 + entry->compressed_data.size()) & 0xFFFFFFFC;
subs[1].dword = (19 + compressed.size()) & 0xFFFFFFFC;
subs[2].dword = 0x00000041;
data += entry->compressed_data;
subs[3].dword = entry->map.map_number.load();
subs[4].dword = compressed.size();
data += compressed;
while (data.size() & 3) {
data.push_back('\0');
}
send_command(l, 0x6C, 0x00, data);
}
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB