Created
May 18, 2022 16:15
-
-
Save tehsausage/73ad61f9cdd418bc0c81698ff88dcc02 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/config/files.ini b/config/files.ini | |
index 5ab9b96..e98e9d8 100644 | |
--- a/config/files.ini | |
+++ b/config/files.ini | |
@@ -6,6 +6,7 @@ | |
## EIF, ENF, ESF, ECF (string) | |
# pub file locations | |
+# "001" will be replaced with the appropriate file number automatically | |
EIF = ./data/pub/dat001.eif | |
ENF = ./data/pub/dtn001.enf | |
ESF = ./data/pub/dsl001.esf | |
@@ -13,7 +14,7 @@ ECF = ./data/pub/dat001.ecf | |
## AutoSplitPubFiles (bool) | |
# Automatically split large pub files in to multiple smaller files | |
-# This is required for more than 900 items to work | |
+# This is required to have more than 900 items when using older editors | |
AutoSplitPubFiles = yes | |
## NewsFile (string) | |
diff --git a/src/eoclient.cpp b/src/eoclient.cpp | |
index 8e12933..1e185c4 100644 | |
--- a/src/eoclient.cpp | |
+++ b/src/eoclient.cpp | |
@@ -293,11 +293,6 @@ void EOClient::Execute(const std::string &data) | |
bool EOClient::Upload(FileType type, int id, InitReply init_reply) | |
{ | |
- bool auto_split = server()->world->config["AutoSplitPubFiles"]; | |
- | |
- if (type != FILE_MAP && id != 1 && !auto_split) | |
- return false; | |
- | |
this->upload_file_id = id; | |
std::string filename; | |
@@ -306,6 +301,29 @@ bool EOClient::Upload(FileType type, int id, InitReply init_reply) | |
std::vector<std::size_t>* file_splits = nullptr; | |
+ auto load_file_splits = [&](auto pub) | |
+ { | |
+ if (pub.files.empty()) | |
+ return false; | |
+ | |
+ Pub_File& first_file = pub.files[0]; | |
+ | |
+ if (pub.files.size() == 1 && first_file.splits.size() > 2) | |
+ { | |
+ filename = first_file.filename; | |
+ file_splits = &first_file.splits; | |
+ } | |
+ else | |
+ { | |
+ if (id - 1 < 0 || id - 1 > pub.files.size()) | |
+ return false; | |
+ | |
+ filename = pub.files[id - 1].filename; | |
+ } | |
+ | |
+ return true; | |
+ }; | |
+ | |
if (type == FILE_MAP) | |
{ | |
char mapbuf[7]; | |
@@ -314,26 +332,26 @@ bool EOClient::Upload(FileType type, int id, InitReply init_reply) | |
} | |
else if (type == FILE_ITEM) | |
{ | |
- file_splits = &this->server()->world->eif->file_splits; | |
- filename = this->server()->world->config["EIF"].GetString(); | |
+ if (!load_file_splits(*this->server()->world->eif)) | |
+ return false; | |
} | |
else if (type == FILE_NPC) | |
{ | |
- file_splits = &this->server()->world->enf->file_splits; | |
- filename = this->server()->world->config["ENF"].GetString(); | |
+ if (!load_file_splits(*this->server()->world->enf)) | |
+ return false; | |
} | |
else if (type == FILE_SPELL) | |
{ | |
- file_splits = &this->server()->world->esf->file_splits; | |
- filename = this->server()->world->config["ESF"].GetString(); | |
+ if (!load_file_splits(*this->server()->world->esf)) | |
+ return false; | |
} | |
else if (type == FILE_CLASS) | |
{ | |
- file_splits = &this->server()->world->ecf->file_splits; | |
- filename = this->server()->world->config["ECF"].GetString(); | |
+ if (!load_file_splits(*this->server()->world->ecf)) | |
+ return false; | |
} | |
- if (auto_split && file_splits) | |
+ if (file_splits) | |
{ | |
if (id < 1 || id >= file_splits->size()) | |
return false; | |
@@ -349,9 +367,14 @@ bool EOClient::Upload(FileType type, const std::string &filename, std::size_t fi | |
{ | |
using std::swap; | |
+ printf("Sending file %s (start=%d, length=%d)\n", filename.c_str(), (int)file_start, (int)file_length); | |
+ | |
if (this->upload_fh) | |
throw std::runtime_error("Already uploading file"); | |
+ if (file_length > 63992) | |
+ throw std::runtime_error("File is too large to send"); | |
+ | |
this->upload_fh = std::fopen(filename.c_str(), "rb"); | |
if (!this->upload_fh) | |
diff --git a/src/eodata.cpp b/src/eodata.cpp | |
index cc4642c..84f90e1 100644 | |
--- a/src/eodata.cpp | |
+++ b/src/eodata.cpp | |
@@ -8,12 +8,35 @@ | |
#include "console.hpp" | |
#include "packet.hpp" | |
-#include <cstdio> | |
#include <cstdlib> | |
+#include <cstring> | |
#include <string> | |
+#include <type_traits> | |
static const char *eodata_safe_fail_filename; | |
+std::string convert_pub_filename(const std::string& s) | |
+{ | |
+ std::string result; | |
+ | |
+ for (const char* p = &s[0]; *p != '\0'; ++p) | |
+ { | |
+ if ((p - &s[0]) < s.size() - 3 && p[0] == '0' && p[1] == '0' && p[2] == '1') | |
+ { | |
+ result += "%03d"; | |
+ p += 2; | |
+ continue; | |
+ } | |
+ | |
+ if (*p == '%') | |
+ result += '%'; | |
+ | |
+ result += *p; | |
+ } | |
+ | |
+ return result; | |
+} | |
+ | |
static void eodata_safe_fail(int line) | |
{ | |
Console::Err("Invalid file / failed read/seek: %s -- %i", eodata_safe_fail_filename, line); | |
@@ -23,243 +46,243 @@ static void eodata_safe_fail(int line) | |
#define SAFE_SEEK(fh, offset, from) if (std::fseek(fh, offset, from) != 0) { std::fclose(fh); eodata_safe_fail(__LINE__); } | |
#define SAFE_READ(buf, size, count, fh) if (std::fread(buf, size, count, fh) != static_cast<std::size_t>(count)) { std::fclose(fh); eodata_safe_fail(__LINE__); } | |
-void EIF::Read(const std::string& filename) | |
+void pub_read_record(EIF_Data& newdata, char* buf) | |
{ | |
- this->data.clear(); | |
- | |
- std::FILE *fh = std::fopen(filename.c_str(), "rb"); | |
- eodata_safe_fail_filename = filename.c_str(); | |
- | |
- if (!fh) | |
+ newdata.graphic = PacketProcessor::Number(buf[0], buf[1]); | |
+ newdata.type = static_cast<EIF::Type>(PacketProcessor::Number(buf[2])); | |
+ newdata.subtype = static_cast<EIF::SubType>(PacketProcessor::Number(buf[3])); | |
+ // Ranged gun hack | |
+ if (newdata.id == 365 && newdata.name == "Gun") | |
{ | |
- Console::Err("Could not load file: %s", filename.c_str()); | |
- std::exit(1); | |
+ newdata.subtype = EIF::Ranged; | |
} | |
+ // / Ranged gun hack | |
+ newdata.special = static_cast<EIF::Special>(PacketProcessor::Number(buf[4])); | |
+ newdata.hp = PacketProcessor::Number(buf[5], buf[6]); | |
+ newdata.tp = PacketProcessor::Number(buf[7], buf[8]); | |
+ newdata.mindam = PacketProcessor::Number(buf[9], buf[10]); | |
+ newdata.maxdam = PacketProcessor::Number(buf[11], buf[12]); | |
+ newdata.accuracy = PacketProcessor::Number(buf[13], buf[14]); | |
+ newdata.evade = PacketProcessor::Number(buf[15], buf[16]); | |
+ newdata.armor = PacketProcessor::Number(buf[17], buf[18]); | |
+ | |
+ newdata.str = PacketProcessor::Number(buf[20]); | |
+ newdata.intl = PacketProcessor::Number(buf[21]); | |
+ newdata.wis = PacketProcessor::Number(buf[22]); | |
+ newdata.agi = PacketProcessor::Number(buf[23]); | |
+ newdata.con = PacketProcessor::Number(buf[24]); | |
+ newdata.cha = PacketProcessor::Number(buf[25]); | |
+ | |
+ newdata.light = PacketProcessor::Number(buf[26]); | |
+ newdata.dark = PacketProcessor::Number(buf[27]); | |
+ newdata.earth = PacketProcessor::Number(buf[28]); | |
+ newdata.air = PacketProcessor::Number(buf[29]); | |
+ newdata.water = PacketProcessor::Number(buf[30]); | |
+ newdata.fire = PacketProcessor::Number(buf[31]); | |
+ | |
+ newdata.scrollmap = PacketProcessor::Number(buf[32], buf[33], buf[34]); | |
+ newdata.scrollx = PacketProcessor::Number(buf[35]); | |
+ newdata.scrolly = PacketProcessor::Number(buf[36]); | |
+ | |
+ newdata.levelreq = PacketProcessor::Number(buf[37], buf[38]); | |
+ newdata.classreq = PacketProcessor::Number(buf[39], buf[40]); | |
+ | |
+ newdata.strreq = PacketProcessor::Number(buf[41], buf[42]); | |
+ newdata.intreq = PacketProcessor::Number(buf[43], buf[44]); | |
+ newdata.wisreq = PacketProcessor::Number(buf[45], buf[46]); | |
+ newdata.agireq = PacketProcessor::Number(buf[47], buf[48]); | |
+ newdata.conreq = PacketProcessor::Number(buf[49], buf[50]); | |
+ newdata.chareq = PacketProcessor::Number(buf[51], buf[52]); | |
+ | |
+ newdata.weight = PacketProcessor::Number(buf[55]); | |
+ | |
+ newdata.size = static_cast<EIF::Size>(PacketProcessor::Number(buf[57])); | |
+} | |
- SAFE_SEEK(fh, 3, SEEK_SET); | |
- SAFE_READ(this->rid.data(), sizeof(char), 4, fh); | |
- SAFE_READ(this->len.data(), sizeof(char), 2, fh); | |
- int numobj = PacketProcessor::Number(this->len[0], this->len[1]); | |
- SAFE_SEEK(fh, 1, SEEK_CUR); | |
- | |
- this->file_splits.reserve(1 + (numobj / EIF::FILE_MAX_ENTRIES)); | |
- | |
- unsigned char namesize; | |
- std::string name; | |
- char buf[EIF::DATA_SIZE] = {0}; | |
- | |
- this->data.resize(numobj+1); | |
- | |
- SAFE_READ(static_cast<void *>(&namesize), sizeof(char), 1, fh); | |
- for (int i = 1; i <= numobj; ++i) | |
- { | |
- EIF_Data& newdata = this->data[i]; | |
- | |
- if ((i - 1) % EIF::FILE_MAX_ENTRIES == 0) | |
- this->file_splits.push_back(std::size_t(std::ftell(fh)) - 1); | |
- | |
- namesize = PacketProcessor::Number(namesize); | |
- name.resize(namesize); | |
- SAFE_READ(&name[0], sizeof(char), namesize, fh); | |
- SAFE_READ(buf, sizeof(char), EIF::DATA_SIZE, fh); | |
- | |
- newdata.id = i; | |
- newdata.name = name; | |
- newdata.graphic = PacketProcessor::Number(buf[0], buf[1]); | |
- newdata.type = static_cast<EIF::Type>(PacketProcessor::Number(buf[2])); | |
- newdata.subtype = static_cast<EIF::SubType>(PacketProcessor::Number(buf[3])); | |
- // Ranged gun hack | |
- if (newdata.id == 365 && newdata.name == "Gun") | |
- { | |
- newdata.subtype = EIF::Ranged; | |
- } | |
- // / Ranged gun hack | |
- newdata.special = static_cast<EIF::Special>(PacketProcessor::Number(buf[4])); | |
- newdata.hp = PacketProcessor::Number(buf[5], buf[6]); | |
- newdata.tp = PacketProcessor::Number(buf[7], buf[8]); | |
- newdata.mindam = PacketProcessor::Number(buf[9], buf[10]); | |
- newdata.maxdam = PacketProcessor::Number(buf[11], buf[12]); | |
- newdata.accuracy = PacketProcessor::Number(buf[13], buf[14]); | |
- newdata.evade = PacketProcessor::Number(buf[15], buf[16]); | |
- newdata.armor = PacketProcessor::Number(buf[17], buf[18]); | |
- | |
- newdata.str = PacketProcessor::Number(buf[20]); | |
- newdata.intl = PacketProcessor::Number(buf[21]); | |
- newdata.wis = PacketProcessor::Number(buf[22]); | |
- newdata.agi = PacketProcessor::Number(buf[23]); | |
- newdata.con = PacketProcessor::Number(buf[24]); | |
- newdata.cha = PacketProcessor::Number(buf[25]); | |
- | |
- newdata.light = PacketProcessor::Number(buf[26]); | |
- newdata.dark = PacketProcessor::Number(buf[27]); | |
- newdata.earth = PacketProcessor::Number(buf[28]); | |
- newdata.air = PacketProcessor::Number(buf[29]); | |
- newdata.water = PacketProcessor::Number(buf[30]); | |
- newdata.fire = PacketProcessor::Number(buf[31]); | |
- | |
- newdata.scrollmap = PacketProcessor::Number(buf[32], buf[33], buf[34]); | |
- newdata.scrollx = PacketProcessor::Number(buf[35]); | |
- newdata.scrolly = PacketProcessor::Number(buf[36]); | |
- | |
- newdata.levelreq = PacketProcessor::Number(buf[37], buf[38]); | |
- newdata.classreq = PacketProcessor::Number(buf[39], buf[40]); | |
- | |
- newdata.strreq = PacketProcessor::Number(buf[41], buf[42]); | |
- newdata.intreq = PacketProcessor::Number(buf[43], buf[44]); | |
- newdata.wisreq = PacketProcessor::Number(buf[45], buf[46]); | |
- newdata.agireq = PacketProcessor::Number(buf[47], buf[48]); | |
- newdata.conreq = PacketProcessor::Number(buf[49], buf[50]); | |
- newdata.chareq = PacketProcessor::Number(buf[51], buf[52]); | |
- | |
- newdata.weight = PacketProcessor::Number(buf[55]); | |
- | |
- newdata.size = static_cast<EIF::Size>(PacketProcessor::Number(buf[57])); | |
- | |
- if (i < numobj && std::fread(static_cast<void *>(&namesize), sizeof(char), 1, fh) != 1) | |
- { | |
- break; | |
- } | |
- } | |
+void pub_read_record(ENF_Data& newdata, char* buf) | |
+{ | |
+ newdata.graphic = PacketProcessor::Number(buf[0], buf[1]); | |
- if (this->data.back().name.compare("eof") == 0) | |
- { | |
- this->data.pop_back(); | |
- } | |
+ newdata.boss = PacketProcessor::Number(buf[3], buf[4]); | |
+ newdata.child = PacketProcessor::Number(buf[5], buf[6]); | |
+ newdata.type = static_cast<ENF::Type>(PacketProcessor::Number(buf[7], buf[8])); | |
+ newdata.vendor_id = PacketProcessor::Number(buf[9], buf[10]); | |
+ newdata.hp = PacketProcessor::Number(buf[11], buf[12], buf[13]); | |
- this->file_splits.push_back(std::size_t(std::ftell(fh))); | |
+ newdata.mindam = PacketProcessor::Number(buf[16], buf[17]); | |
+ newdata.maxdam = PacketProcessor::Number(buf[18], buf[19]); | |
- Console::Out("%i items loaded.", this->data.size()-1); | |
+ newdata.accuracy = PacketProcessor::Number(buf[20], buf[21]); | |
+ newdata.evade = PacketProcessor::Number(buf[22], buf[23]); | |
+ newdata.armor = PacketProcessor::Number(buf[24], buf[25]); | |
- std::fclose(fh); | |
+ newdata.exp = PacketProcessor::Number(buf[36], buf[37]); | |
} | |
-EIF_Data& EIF::Get(unsigned int id) | |
+void pub_read_record(ESF_Data& newdata, char* buf) | |
{ | |
- if (id < this->data.size()) | |
- return this->data[id]; | |
- else | |
- return this->data[0]; | |
+ newdata.icon = PacketProcessor::Number(buf[0], buf[1]); | |
+ newdata.graphic = PacketProcessor::Number(buf[2], buf[3]); | |
+ newdata.tp = PacketProcessor::Number(buf[4], buf[5]); | |
+ newdata.sp = PacketProcessor::Number(buf[6], buf[7]); | |
+ newdata.cast_time = PacketProcessor::Number(buf[8]); | |
+ | |
+ newdata.type = static_cast<ESF::Type>(PacketProcessor::Number(buf[11])); | |
+ newdata.target_restrict = static_cast<ESF::TargetRestrict>(PacketProcessor::Number(buf[17])); | |
+ newdata.target = static_cast<ESF::Target>(PacketProcessor::Number(buf[18])); | |
+ | |
+ newdata.mindam = PacketProcessor::Number(buf[23], buf[24]); | |
+ newdata.maxdam = PacketProcessor::Number(buf[25], buf[26]); | |
+ newdata.accuracy = PacketProcessor::Number(buf[27], buf[28]); | |
+ newdata.hp = PacketProcessor::Number(buf[34], buf[35]); | |
} | |
-const EIF_Data& EIF::Get(unsigned int id) const | |
+void pub_read_record(ECF_Data& newdata, char* buf) | |
{ | |
- if (id < this->data.size()) | |
- return this->data[id]; | |
- else | |
- return this->data[0]; | |
+ newdata.base = PacketProcessor::Number(buf[0]); | |
+ newdata.type = PacketProcessor::Number(buf[1]); | |
+ | |
+ newdata.str = PacketProcessor::Number(buf[2], buf[3]); | |
+ newdata.intl = PacketProcessor::Number(buf[4], buf[5]); | |
+ newdata.wis = PacketProcessor::Number(buf[6], buf[7]); | |
+ newdata.agi = PacketProcessor::Number(buf[8], buf[9]); | |
+ newdata.con = PacketProcessor::Number(buf[10], buf[11]); | |
+ newdata.cha = PacketProcessor::Number(buf[12], buf[13]); | |
} | |
-unsigned int EIF::GetKey(int keynum) const | |
+template <class T> | |
+int read_single_file(T& pub, Pub_File& file, bool auto_split, int version, int first_id) | |
{ | |
- for (std::size_t i = 0; i < this->data.size(); ++i) | |
+ std::FILE *fh = std::fopen(file.filename.c_str(), "rb"); | |
+ eodata_safe_fail_filename = file.filename.c_str(); | |
+ | |
+ if (!fh) | |
{ | |
- if (this->Get(i).type == EIF::Key && this->Get(i).key == keynum) | |
- return i; | |
+ Console::Err("Could not load file: %s", file.filename.c_str()); | |
+ std::exit(1); | |
} | |
- return 0; | |
-} | |
+ // Only attempt to auto-split the first file | |
+ if (first_id != 1) | |
+ auto_split = false; | |
-void ENF::Read(const std::string& filename) | |
-{ | |
- this->data.clear(); | |
+ unsigned char namesize, shoutsize; | |
+ std::string name, shout; | |
+ char buf[T::DATA_SIZE] = {0}; | |
- std::FILE *fh = std::fopen(filename.c_str(), "rb"); | |
- eodata_safe_fail_filename = filename.c_str(); | |
+ int readobj = 0; | |
+ int max_entries = T::FILE_MAX_ENTRIES; | |
- if (!fh) | |
+ if constexpr (std::is_same_v<T, ECF>) | |
{ | |
- Console::Err("Could not load file: %s", filename.c_str()); | |
- std::exit(1); | |
+ if (version >= 1) | |
+ max_entries = T::FILE_MAX_ENTRIES_V2; | |
} | |
- SAFE_SEEK(fh, 3, SEEK_SET); | |
- SAFE_READ(this->rid.data(), sizeof(char), 4, fh); | |
- SAFE_READ(this->len.data(), sizeof(char), 2, fh); | |
- int numobj = PacketProcessor::Number(this->len[0], this->len[1]); | |
- SAFE_SEEK(fh, 1, SEEK_CUR); | |
+ if (version < 1 && auto_split == true) | |
+ max_entries = 64000; | |
- this->file_splits.reserve(1 + (numobj / ENF::FILE_MAX_ENTRIES)); | |
+ SAFE_SEEK(fh, 10, SEEK_SET); | |
+ SAFE_READ(static_cast<void *>(&namesize), sizeof(char), 1, fh); | |
- unsigned char namesize; | |
- std::string name; | |
- char buf[ENF::DATA_SIZE] = {0}; | |
+ if constexpr (std::is_same_v<T, ESF>) | |
+ { | |
+ SAFE_READ(static_cast<void *>(&shoutsize), sizeof(char), 1, fh); | |
+ } | |
- this->data.resize(numobj+1); | |
+ file.splits.reserve(4); | |
- SAFE_READ(static_cast<void *>(&namesize), sizeof(char), 1, fh); | |
- for (int i = 1; i <= numobj; ++i) | |
+ for (int i = 0; i < max_entries; ++i) | |
{ | |
- ENF_Data& newdata = this->data[i]; | |
+ if (auto_split && (i % T::FILE_MAX_ENTRIES == 0)) | |
+ { | |
+ std::size_t split = std::size_t(std::ftell(fh)) - 1; | |
+ int size = (int)split - (file.splits.empty() ? 0 : (int)file.splits.back()); | |
+ | |
+ if (size > 63992) | |
+ Console::Err("Auto-split file is too large (%d bytes too long): %s", size - 63992, file.filename.c_str()); | |
- if ((i - 1) % ENF::FILE_MAX_ENTRIES == 0) | |
- this->file_splits.push_back(std::size_t(std::ftell(fh)) - 1); | |
+ file.splits.push_back(std::size_t(std::ftell(fh)) - 1); | |
+ } | |
namesize = PacketProcessor::Number(namesize); | |
name.resize(namesize); | |
- SAFE_READ(&name[0], sizeof(char), namesize, fh); | |
- SAFE_READ(buf, sizeof(char), ENF::DATA_SIZE, fh); | |
- newdata.id = i; | |
- newdata.name = name; | |
+ if (namesize > 0) | |
+ SAFE_READ(&name[0], sizeof(char), namesize, fh); | |
- newdata.graphic = PacketProcessor::Number(buf[0], buf[1]); | |
+ if constexpr (std::is_same_v<T, ESF>) | |
+ { | |
+ shoutsize = PacketProcessor::Number(shoutsize); | |
+ shout.resize(shoutsize); | |
- newdata.boss = PacketProcessor::Number(buf[3], buf[4]); | |
- newdata.child = PacketProcessor::Number(buf[5], buf[6]); | |
- newdata.type = static_cast<ENF::Type>(PacketProcessor::Number(buf[7], buf[8])); | |
- newdata.vendor_id = PacketProcessor::Number(buf[9], buf[10]); | |
- newdata.hp = PacketProcessor::Number(buf[11], buf[12], buf[13]); | |
+ if (shoutsize > 0) | |
+ SAFE_READ(&shout[0], sizeof(char), shoutsize, fh); | |
+ } | |
- newdata.mindam = PacketProcessor::Number(buf[16], buf[17]); | |
- newdata.maxdam = PacketProcessor::Number(buf[18], buf[19]); | |
+ SAFE_READ(buf, sizeof(char), T::DATA_SIZE, fh); | |
- newdata.accuracy = PacketProcessor::Number(buf[20], buf[21]); | |
- newdata.evade = PacketProcessor::Number(buf[22], buf[23]); | |
- newdata.armor = PacketProcessor::Number(buf[24], buf[25]); | |
+ typename T::data_t& newdata = pub.data[first_id + i]; | |
- newdata.exp = PacketProcessor::Number(buf[36], buf[37]); | |
+ ++readobj; | |
- if (i < numobj && std::fread(static_cast<void *>(&namesize), sizeof(char), 1, fh) != 1) | |
+ newdata.id = first_id + i; | |
+ newdata.name = name; | |
+ | |
+ if constexpr (std::is_same_v<T, ESF>) | |
+ { | |
+ newdata.shout = shout; | |
+ } | |
+ | |
+ pub_read_record(newdata, buf); | |
+ | |
+ if (first_id + i >= pub.data.size() | |
+ || std::fread(static_cast<void *>(&namesize), sizeof(char), 1, fh) != 1) | |
{ | |
break; | |
} | |
+ | |
+ if constexpr (std::is_same_v<T, ESF>) | |
+ { | |
+ if (std::fread(static_cast<void *>(&shoutsize), sizeof(char), 1, fh) != 1) | |
+ { | |
+ break; | |
+ } | |
+ } | |
} | |
- if (this->data.back().name.compare("eof") == 0) | |
+ std::size_t last_split = std::size_t(std::ftell(fh)); | |
+ int size = (int)last_split - (int)file.splits.back(); | |
+ | |
+ if (size > 63992) | |
{ | |
- this->data.pop_back(); | |
+ if (auto_split && file.splits.size() > 1) | |
+ Console::Err("Auto-split file is too large (%d bytes too long): %s", size - 63992, file.filename.c_str()); | |
+ else | |
+ Console::Err("File is too large (%d bytes too long): %s", size - 63992, file.filename.c_str()); | |
} | |
- this->file_splits.push_back(std::size_t(std::ftell(fh))); | |
- | |
- Console::Out("%i npc types loaded.", this->data.size()-1); | |
+ file.splits.push_back(last_split); | |
std::fclose(fh); | |
-} | |
-ENF_Data& ENF::Get(unsigned int id) | |
-{ | |
- if (id < this->data.size()) | |
- return this->data[id]; | |
- else | |
- return this->data[0]; | |
+ return readobj; | |
} | |
-const ENF_Data& ENF::Get(unsigned int id) const | |
+template <class T> | |
+void read_pub_files(T& pub, const std::string& filename, bool auto_split) | |
{ | |
- if (id < this->data.size()) | |
- return this->data[id]; | |
- else | |
- return this->data[0]; | |
-} | |
+ pub.files.clear(); | |
+ pub.data.clear(); | |
-void ESF::Read(const std::string& filename) | |
-{ | |
- this->data.clear(); | |
+ std::string filename_template = convert_pub_filename(filename); | |
+ char fn_buf[256]; | |
+ int file_number = 1; | |
+ | |
+ std::snprintf(fn_buf, sizeof fn_buf, filename_template.c_str(), file_number); | |
- std::FILE *fh = std::fopen(filename.c_str(), "rb"); | |
+ std::FILE *fh = std::fopen(fn_buf, "rb"); | |
eodata_safe_fail_filename = filename.c_str(); | |
if (!fh) | |
@@ -268,88 +291,41 @@ void ESF::Read(const std::string& filename) | |
std::exit(1); | |
} | |
- SAFE_SEEK(fh, 3, SEEK_SET); | |
- SAFE_READ(this->rid.data(), sizeof(char), 4, fh); | |
- SAFE_READ(this->len.data(), sizeof(char), 2, fh); | |
- int numobj = PacketProcessor::Number(this->len[0], this->len[1]); | |
- SAFE_SEEK(fh, 1, SEEK_CUR); | |
+ char header_buf[10]; | |
+ SAFE_READ(header_buf, sizeof(char), 10, fh); | |
- this->file_splits.reserve(1 + (numobj / ESF::FILE_MAX_ENTRIES)); | |
+ std::memcpy(pub.rid.data(), header_buf + 3, 4); | |
+ std::memcpy(pub.len.data(), header_buf + 7, 2); | |
- unsigned char namesize, shoutsize; | |
- std::string name, shout; | |
- char buf[ESF::DATA_SIZE] = {0}; | |
+ int readobj = 0; | |
+ int numobj = PacketProcessor::Number(pub.len[0], pub.len[1]); | |
+ int version = PacketProcessor::Number(header_buf[9]); | |
- this->data.resize(numobj+1); | |
+ pub.data.resize(numobj + 1); | |
- SAFE_READ(static_cast<void *>(&namesize), sizeof(char), 1, fh); | |
- SAFE_READ(static_cast<void *>(&shoutsize), sizeof(char), 1, fh); | |
- for (int i = 1; i <= numobj; ++i) | |
+ while (readobj < numobj && file_number < 1000) | |
{ | |
- ESF_Data& newdata = this->data[i]; | |
- | |
- if ((i - 1) % ESF::FILE_MAX_ENTRIES == 0) | |
- this->file_splits.push_back(std::size_t(std::ftell(fh)) - 2); | |
- | |
- namesize = PacketProcessor::Number(namesize); | |
- name.resize(namesize); | |
- if (namesize > 0) | |
- { | |
- SAFE_READ(&name[0], sizeof(char), namesize, fh); | |
- } | |
- | |
- shoutsize = PacketProcessor::Number(shoutsize); | |
- shout.resize(shoutsize); | |
- if (shoutsize > 0) | |
- { | |
- SAFE_READ(&shout[0], sizeof(char), shoutsize, fh); | |
- } | |
+ std::snprintf(fn_buf, sizeof fn_buf, filename_template.c_str(), file_number++); | |
+ pub.files.push_back(Pub_File{fn_buf, {}}); | |
+ Pub_File& pub_file = pub.files.back(); | |
+ readobj += read_single_file(pub, pub_file, auto_split, version, readobj + 1); | |
- SAFE_READ(buf, sizeof(char), ESF::DATA_SIZE, fh); | |
- | |
- newdata.id = i; | |
- newdata.name = name; | |
- newdata.shout = shout; | |
- | |
- newdata.icon = PacketProcessor::Number(buf[0], buf[1]); | |
- newdata.graphic = PacketProcessor::Number(buf[2], buf[3]); | |
- newdata.tp = PacketProcessor::Number(buf[4], buf[5]); | |
- newdata.sp = PacketProcessor::Number(buf[6], buf[7]); | |
- newdata.cast_time = PacketProcessor::Number(buf[8]); | |
- | |
- newdata.type = static_cast<ESF::Type>(PacketProcessor::Number(buf[11])); | |
- newdata.target_restrict = static_cast<ESF::TargetRestrict>(PacketProcessor::Number(buf[17])); | |
- newdata.target = static_cast<ESF::Target>(PacketProcessor::Number(buf[18])); | |
- | |
- newdata.mindam = PacketProcessor::Number(buf[23], buf[24]); | |
- newdata.maxdam = PacketProcessor::Number(buf[25], buf[26]); | |
- newdata.accuracy = PacketProcessor::Number(buf[27], buf[28]); | |
- newdata.hp = PacketProcessor::Number(buf[34], buf[35]); | |
- | |
- if (i < numobj && std::fread(static_cast<void *>(&namesize), sizeof(char), 1, fh) != 1) | |
- { | |
+ // Only a single file is loaded if auto-splitting occurs | |
+ if (pub_file.splits.size() > 2) | |
break; | |
- } | |
- | |
- if (i < numobj && std::fread(static_cast<void *>(&shoutsize), sizeof(char), 1, fh) != 1) | |
- { | |
- break; | |
- } | |
} | |
- if (this->data.back().name.compare("eof") == 0) | |
- { | |
- this->data.pop_back(); | |
- } | |
- | |
- this->file_splits.push_back(std::size_t(std::ftell(fh))); | |
- | |
- Console::Out("%i spells loaded.", this->data.size()-1); | |
+ if (version < 1 && pub.data.back().name == "eof") | |
+ pub.data.pop_back(); | |
+} | |
- std::fclose(fh); | |
+void EIF::Read(const std::string& filename, bool auto_split) | |
+{ | |
+ read_pub_files(*this, filename, auto_split); | |
+ Console::Out("%i items loaded.", this->data.size()-1); | |
} | |
-ESF_Data& ESF::Get(unsigned int id) | |
+EIF_Data& EIF::Get(unsigned int id) | |
{ | |
if (id < this->data.size()) | |
return this->data[id]; | |
@@ -357,7 +333,7 @@ ESF_Data& ESF::Get(unsigned int id) | |
return this->data[0]; | |
} | |
-const ESF_Data& ESF::Get(unsigned int id) const | |
+const EIF_Data& EIF::Get(unsigned int id) const | |
{ | |
if (id < this->data.size()) | |
return this->data[id]; | |
@@ -365,76 +341,65 @@ const ESF_Data& ESF::Get(unsigned int id) const | |
return this->data[0]; | |
} | |
-void ECF::Read(const std::string& filename) | |
+unsigned int EIF::GetKey(int keynum) const | |
{ | |
- this->data.clear(); | |
- | |
- std::FILE *fh = std::fopen(filename.c_str(), "rb"); | |
- eodata_safe_fail_filename = filename.c_str(); | |
- | |
- if (!fh) | |
+ for (std::size_t i = 0; i < this->data.size(); ++i) | |
{ | |
- Console::Err("Could not load file: %s", filename.c_str()); | |
- std::exit(1); | |
+ if (this->Get(i).type == EIF::Key && this->Get(i).key == keynum) | |
+ return i; | |
} | |
- SAFE_SEEK(fh, 3, SEEK_SET); | |
- SAFE_READ(this->rid.data(), sizeof(char), 4, fh); | |
- SAFE_READ(this->len.data(), sizeof(char), 2, fh); | |
- int numobj = PacketProcessor::Number(this->len[0], this->len[1]); | |
- SAFE_SEEK(fh, 1, SEEK_CUR); | |
- | |
- this->file_splits.reserve(1 + (numobj / ECF::FILE_MAX_ENTRIES)); | |
- | |
- unsigned char namesize; | |
- std::string name; | |
- char buf[ECF::DATA_SIZE] = {0}; | |
- | |
- this->data.resize(numobj+1); | |
- | |
- SAFE_READ(static_cast<void *>(&namesize), sizeof(char), 1, fh); | |
- for (int i = 1; i <= numobj; ++i) | |
- { | |
- ECF_Data& newdata = this->data[i]; | |
- | |
- if ((i - 1) % ECF::FILE_MAX_ENTRIES == 0) | |
- this->file_splits.push_back(std::size_t(std::ftell(fh)) - 1); | |
- | |
- namesize = PacketProcessor::Number(namesize); | |
- name.resize(namesize); | |
- SAFE_READ(&name[0], sizeof(char), namesize, fh); | |
- | |
- SAFE_READ(buf, sizeof(char), ECF::DATA_SIZE, fh); | |
+ return 0; | |
+} | |
- newdata.id = i; | |
- newdata.name = name; | |
+void ENF::Read(const std::string& filename, bool auto_split) | |
+{ | |
+ read_pub_files(*this, filename, auto_split); | |
+ Console::Out("%i npc types loaded.", this->data.size()-1); | |
+} | |
- newdata.base = PacketProcessor::Number(buf[0]); | |
- newdata.type = PacketProcessor::Number(buf[1]); | |
+ENF_Data& ENF::Get(unsigned int id) | |
+{ | |
+ if (id < this->data.size()) | |
+ return this->data[id]; | |
+ else | |
+ return this->data[0]; | |
+} | |
- newdata.str = PacketProcessor::Number(buf[2], buf[3]); | |
- newdata.intl = PacketProcessor::Number(buf[4], buf[5]); | |
- newdata.wis = PacketProcessor::Number(buf[6], buf[7]); | |
- newdata.agi = PacketProcessor::Number(buf[8], buf[9]); | |
- newdata.con = PacketProcessor::Number(buf[10], buf[11]); | |
- newdata.cha = PacketProcessor::Number(buf[12], buf[13]); | |
+const ENF_Data& ENF::Get(unsigned int id) const | |
+{ | |
+ if (id < this->data.size()) | |
+ return this->data[id]; | |
+ else | |
+ return this->data[0]; | |
+} | |
- if (i < numobj && std::fread(static_cast<void *>(&namesize), sizeof(char), 1, fh) != 1) | |
- { | |
- break; | |
- } | |
- } | |
+void ESF::Read(const std::string& filename, bool auto_split) | |
+{ | |
+ read_pub_files(*this, filename, auto_split); | |
+ Console::Out("%i spells loaded.", this->data.size()-1); | |
+} | |
- if (this->data.back().name.compare("eof") == 0) | |
- { | |
- this->data.pop_back(); | |
- } | |
+ESF_Data& ESF::Get(unsigned int id) | |
+{ | |
+ if (id < this->data.size()) | |
+ return this->data[id]; | |
+ else | |
+ return this->data[0]; | |
+} | |
- this->file_splits.push_back(std::size_t(std::ftell(fh))); | |
+const ESF_Data& ESF::Get(unsigned int id) const | |
+{ | |
+ if (id < this->data.size()) | |
+ return this->data[id]; | |
+ else | |
+ return this->data[0]; | |
+} | |
+void ECF::Read(const std::string& filename, bool auto_split) | |
+{ | |
+ read_pub_files(*this, filename, auto_split); | |
Console::Out("%i classes loaded.", this->data.size()-1); | |
- | |
- std::fclose(fh); | |
} | |
ECF_Data& ECF::Get(unsigned int id) | |
diff --git a/src/eodata.hpp b/src/eodata.hpp | |
index 10be5f0..59555d7 100644 | |
--- a/src/eodata.hpp | |
+++ b/src/eodata.hpp | |
@@ -12,6 +12,12 @@ | |
#include <string> | |
#include <vector> | |
+struct Pub_File | |
+{ | |
+ std::string filename; | |
+ std::vector<std::size_t> splits; | |
+}; | |
+ | |
/** | |
* One item record in an EIF object | |
*/ | |
@@ -177,16 +183,17 @@ class EIF | |
} | |
} | |
+ using data_t = EIF_Data; | |
static const int DATA_SIZE = 58; | |
static const int FILE_MAX_ENTRIES = 900; | |
std::array<unsigned char, 4> rid; | |
std::array<unsigned char, 2> len; | |
std::vector<EIF_Data> data; | |
- std::vector<std::size_t> file_splits; | |
+ std::vector<Pub_File> files; | |
- EIF(const std::string& filename) { Read(filename.c_str()); } | |
+ EIF(const std::string& filename, bool auto_split) { Read(filename.c_str(), auto_split); } | |
- void Read(const std::string& filename); | |
+ void Read(const std::string& filename, bool auto_split); | |
EIF_Data& Get(unsigned int id); | |
const EIF_Data& Get(unsigned int id) const; | |
@@ -253,16 +260,17 @@ class ENF | |
Quest | |
}; | |
+ using data_t = ENF_Data; | |
static const int DATA_SIZE = 39; | |
static const int FILE_MAX_ENTRIES = 900; | |
std::array<unsigned char, 4> rid; | |
std::array<unsigned char, 2> len; | |
std::vector<ENF_Data> data; | |
- std::vector<std::size_t> file_splits; | |
+ std::vector<Pub_File> files; | |
- ENF(const std::string& filename) { Read(filename.c_str()); } | |
+ ENF(const std::string& filename, bool auto_split) { Read(filename.c_str(), auto_split); } | |
- void Read(const std::string& filename); | |
+ void Read(const std::string& filename, bool auto_split); | |
ENF_Data& Get(unsigned int id); | |
const ENF_Data& Get(unsigned int id) const; | |
@@ -331,16 +339,17 @@ class ESF | |
Group | |
}; | |
+ using data_t = ESF_Data; | |
static const int DATA_SIZE = 51; | |
static const int FILE_MAX_ENTRIES = 900; | |
std::array<unsigned char, 4> rid; | |
std::array<unsigned char, 2> len; | |
std::vector<ESF_Data> data; | |
- std::vector<std::size_t> file_splits; | |
+ std::vector<Pub_File> files; | |
- ESF(const std::string& filename) { Read(filename.c_str()); } | |
+ ESF(const std::string& filename, bool auto_split) { Read(filename.c_str(), auto_split); } | |
- void Read(const std::string& filename); | |
+ void Read(const std::string& filename, bool auto_split); | |
ESF_Data& Get(unsigned int id); | |
const ESF_Data& Get(unsigned int id) const; | |
@@ -378,16 +387,18 @@ template <class ECF> struct ECF_Data_Base | |
class ECF | |
{ | |
public: | |
+ using data_t = ECF_Data; | |
static const int DATA_SIZE = 14; | |
static const int FILE_MAX_ENTRIES = 250; | |
+ static const int FILE_MAX_ENTRIES_V2 = 900; | |
std::array<unsigned char, 4> rid; | |
std::array<unsigned char, 2> len; | |
std::vector<ECF_Data> data; | |
- std::vector<std::size_t> file_splits; | |
+ std::vector<Pub_File> files; | |
- ECF(const std::string& filename) { Read(filename.c_str()); } | |
+ ECF(const std::string& filename, bool auto_split) { Read(filename.c_str(), auto_split); } | |
- void Read(const std::string& filename); | |
+ void Read(const std::string& filename, bool auto_split); | |
ECF_Data& Get(unsigned int id); | |
const ECF_Data& Get(unsigned int id) const; | |
diff --git a/src/world.cpp b/src/world.cpp | |
index 20efe7b..49fd96a 100644 | |
--- a/src/world.cpp | |
+++ b/src/world.cpp | |
@@ -403,10 +403,12 @@ World::World(std::array<std::string, 6> dbinfo, const Config &eoserv_config, con | |
this->UpdateConfig(); | |
this->LoadHome(); | |
- this->eif = new EIF(this->config["EIF"]); | |
- this->enf = new ENF(this->config["ENF"]); | |
- this->esf = new ESF(this->config["ESF"]); | |
- this->ecf = new ECF(this->config["ECF"]); | |
+ bool auto_split = this->config["AutoSplitPubFiles"]; | |
+ | |
+ this->eif = new EIF(this->config["EIF"], auto_split); | |
+ this->enf = new ENF(this->config["ENF"], auto_split); | |
+ this->esf = new ESF(this->config["ESF"], auto_split); | |
+ this->ecf = new ECF(this->config["ECF"], auto_split); | |
std::size_t num_npcs = this->enf->data.size(); | |
this->npc_data.resize(num_npcs); | |
@@ -915,10 +917,12 @@ void World::ReloadPub(bool quiet) | |
auto esf_id = this->esf->rid; | |
auto ecf_id = this->ecf->rid; | |
- this->eif->Read(this->config["EIF"]); | |
- this->enf->Read(this->config["ENF"]); | |
- this->esf->Read(this->config["ESF"]); | |
- this->ecf->Read(this->config["ECF"]); | |
+ bool auto_split = this->config["AutoSplitPubFiles"]; | |
+ | |
+ this->eif->Read(this->config["EIF"], auto_split); | |
+ this->enf->Read(this->config["ENF"], auto_split); | |
+ this->esf->Read(this->config["ESF"], auto_split); | |
+ this->ecf->Read(this->config["ECF"], auto_split); | |
if (eif_id != this->eif->rid || enf_id != this->enf->rid | |
|| esf_id != this->esf->rid || ecf_id != this->ecf->rid) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment