diff --git a/dep/dbmgr b/dep/dbmgr index 2d884b8..a2768a0 160000 --- a/dep/dbmgr +++ b/dep/dbmgr @@ -1 +1 @@ -Subproject commit 2d884b843a057c25159833226a4e2c73443d91eb +Subproject commit a2768a05d85958cb52de967a1ef2ea20d9bba309 diff --git a/src/common/common.cpp b/src/common/common.cpp index 82be229..62987f1 100644 --- a/src/common/common.cpp +++ b/src/common/common.cpp @@ -40,7 +40,10 @@ std::vector readFile(const std::string &fn) { FILE *f = fopen(fn.c_str(), "rb"); if (!f) + { + printf("Cannot open file %s\n", fn.c_str()); throw std::runtime_error("Cannot open file " + fn); + } fseek(f, 0, SEEK_END); auto sz = ftell(f); fseek(f, 0, SEEK_SET); @@ -64,121 +67,141 @@ buffer::buffer() } buffer::buffer(size_t size) - : buf(new std::vector(size)) + : buf_(new std::vector(size)) { - this->size = buf->size(); + size_ = buf_->size(); skip(0); } buffer::buffer(const std::vector &buf, uint32_t data_offset) - : buf(new std::vector(buf)), data_offset(data_offset) + : buf_(new std::vector(buf)), data_offset(data_offset) { skip(0); - size = this->buf->size(); + size_ = buf_->size(); } buffer::buffer(buffer &rhs, uint32_t size) - : buf(rhs.buf) + : buf_(rhs.buf_) { - index = rhs.index; + index_ = rhs.index_; data_offset = rhs.data_offset; ptr = rhs.ptr; - this->size = index + size; + size_ = index_ + size; rhs.skip(size); } buffer::buffer(buffer &rhs, uint32_t size, uint32_t offset) - : buf(rhs.buf) + : buf_(rhs.buf_) { - index = offset; + index_ = offset; data_offset = offset; - ptr = (uint8_t *)buf->data() + index; - this->size = index + size; + ptr = (uint8_t *)buf_->data() + index_; + size_ = index_ + size; } uint32_t buffer::read(void *dst, uint32_t size, bool nothrow) const { - if (!buf) + if (!buf_) throw std::logic_error("buffer: not initialized"); - if (index >= this->size) + if (index_ >= size_) { if (nothrow) return 0; throw std::logic_error("buffer: out of range"); } - if (index + size > this->size) + if (index_ + size > size_) { if (!nothrow) throw std::logic_error("buffer: too much data"); - size = this->size - index; + size = size_ - index_; } - memcpy(dst, buf->data() + index, size); + memcpy(dst, buf_->data() + index_, size); skip(size); return size; } +uint32_t buffer::readfrom(void *dst, uint32_t size, uint32_t offset, bool nothrow) const +{ + if (!buf_) + throw std::logic_error("buffer: not initialized"); + if (offset + size > size_) + { + if (!nothrow) + throw std::logic_error("buffer: too much data"); + size = size_ - offset; + } + memcpy(dst, buf_->data() + offset, size); + return size; +} + uint32_t buffer::write(const void *src, uint32_t size, bool nothrow) { - if (!buf) + if (!buf_) { - buf = std::make_shared>(size); - this->size = buf->size(); + buf_ = std::make_shared>(size); + size_ = buf_->size(); } - if (index > this->size) + if (index_ > size_) { if (nothrow) return 0; throw std::logic_error("buffer: out of range"); } - if (index + size > this->size) + if (index_ + size > size_) { - buf->resize(index + size); - this->size = buf->size(); + buf_->resize(index_ + size); + size_ = buf_->size(); } - memcpy((uint8_t *)buf->data() + index, src, size); + memcpy((uint8_t *)buf_->data() + index_, src, size); skip(size); return size; } void buffer::skip(int n) const { - if (!buf) + if (!buf_) throw std::logic_error("buffer: not initialized"); - index += n; + index_ += n; data_offset += n; - ptr = (uint8_t *)buf->data() + index; + ptr = (uint8_t *)buf_->data() + index_; } void buffer::reset() const { - index = 0; + index_ = 0; data_offset = 0; - ptr = (uint8_t *)buf->data(); + ptr = (uint8_t *)buf_->data(); +} + +void buffer::seek(uint32_t size) const +{ + reset(); + skip(size); } bool buffer::check(int index) const { - return this->index == index; + return index_ == index; } bool buffer::eof() const { - return check(this->size); + return check(size_); } -uint32_t buffer::getIndex() const +uint32_t buffer::index() const { - return index; + return index_; } -uint32_t buffer::getSize() const +uint32_t buffer::size() const { - return this->size; + return size_; } -const std::vector &buffer::getBuf() const +const std::vector &buffer::buf() const { - if (!buf) + if (!buf_) throw std::logic_error("buffer: not initialized"); - return *buf; + return *buf_; } diff --git a/src/common/common.h b/src/common/common.h index 1a8f4de..811cfa5 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -42,26 +42,28 @@ public: buffer(buffer &rhs, uint32_t size, uint32_t offset); uint32_t read(void *dst, uint32_t size, bool nothrow = false) const; + uint32_t readfrom(void *dst, uint32_t size, uint32_t offset, bool nothrow = false) const; uint32_t write(const void *src, uint32_t size, bool nothrow = false); template - uint32_t write(const T &src, bool nothrow = false) + uint32_t write(const T &src) { - return write(&src, sizeof(src), nothrow); + return write(&src, sizeof(src)); } + void seek(uint32_t size) const; void skip(int n) const; bool eof() const; bool check(int index) const; void reset() const; - uint32_t getIndex() const; - uint32_t getSize() const; - const std::vector &getBuf() const; + uint32_t index() const; + uint32_t size() const; + const std::vector &buf() const; private: - std::shared_ptr> buf; - mutable uint32_t index = 0; + std::shared_ptr> buf_; + mutable uint32_t index_ = 0; mutable uint8_t *ptr = 0; mutable uint32_t data_offset = 0; - mutable uint32_t size = 0; + mutable uint32_t size_ = 0; }; diff --git a/src/mod_converter/mod_converter.bat b/src/mod_converter/mod_converter.bat new file mode 100644 index 0000000..44853dc --- /dev/null +++ b/src/mod_converter/mod_converter.bat @@ -0,0 +1 @@ +python mod_converter.py --dir "h:\\Games\\Epic Games\\Projects\\AIM\\models1\\" \ No newline at end of file diff --git a/src/mod_converter/mod_converter.cpp b/src/mod_converter/mod_converter.cpp index 2458cbd..d69f96a 100644 --- a/src/mod_converter/mod_converter.cpp +++ b/src/mod_converter/mod_converter.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -27,6 +28,48 @@ using namespace std; +// options +bool silent = false; +bool printMaxPolygonBlock = false; +string filename; + +bool parse_cmd(int argc, char *argv[]); + +void print(const block &b, const std::string &fn) +{ + if (b.type == BlockType::ParticleEmitter) + return; + + auto obj_fn = fn; + if (!printMaxPolygonBlock) + obj_fn += string(".") + b.name; + obj_fn += ".obj"; + ofstream o(obj_fn); + o << "#" << "\n"; + o << "# A.I.M. Model Converter (ver. " << version() << ")\n"; + o << "#" << "\n"; + o << "\n"; + int p1 = fn.rfind("\\"); + int p2 = fn.rfind("/"); + auto mtl = fn.substr(std::max(p1, p2) + 1); + if (!printMaxPolygonBlock) + mtl += string(".") + b.name; + o << "mtllib " << mtl << ".mtl\n"; + o << "\n"; + o << b.printObj(mtl); + + auto mtl_fn = fn; + if (!printMaxPolygonBlock) + mtl_fn += string(".") + b.name; + mtl_fn += ".mtl"; + ofstream m(mtl_fn); + m << "#" << "\n"; + m << "# A.I.M. Model Converter (ver. " << version() << ")\n"; + m << "#" << "\n"; + m << "\n"; + m << b.printMtl(mtl); +} + void convert_model(string fn) { buffer b(readFile(fn)); @@ -36,26 +79,47 @@ void convert_model(string fn) if (!b.eof()) { stringstream ss; - ss << hex << b.getIndex() << " != " << hex << b.getSize(); + ss << hex << b.index() << " != " << hex << b.size(); throw std::logic_error(ss.str()); } - m.writeObj(fn); + // write obj and mtl + if (printMaxPolygonBlock) + { + int max = 0; + int maxBlock = -1; + for (int i = 0; i < m.blocks.size(); i++) + { + if (m.blocks[i].n_vertex > max) + { + max = m.blocks[i].n_vertex; + maxBlock = i; + } + } + print(m.blocks[maxBlock], filename); + } + else + { + for (auto &f : m.blocks) + print(f, filename); + } } int main(int argc, char *argv[]) try { - if (argc != 2) + if (argc < 2 || !parse_cmd(argc, argv)) { - printf("Usage: %s model_file\n", argv[0]); + printf("Usage: %s [OPTIONS] model_file\n", argv[0]); return 1; } - convert_model(argv[1]); + convert_model(filename); return 0; } catch (std::runtime_error &e) { + if (silent) + return 1; string error; if (argv[1]) error += argv[1]; @@ -72,13 +136,42 @@ catch (std::runtime_error &e) } catch (std::exception &e) { + if (silent) + return 1; printf("%s\n", argv[1]); printf("error: %s\n", e.what()); return 1; } catch (...) { + if (silent) + return 1; printf("%s\n", argv[1]); printf("error: unknown exception\n"); return 1; } + +bool parse_cmd(int argc, char *argv[]) +{ + for (int i = 1; i < argc; i++) + { + auto arg = argv[i]; + if (*arg != '-') + { + if (i != argc - 1) + return false; + filename = arg; + continue; + } + switch (arg[1]) + { + case 's': + silent = true; + break; + case 'm': + printMaxPolygonBlock = true; + break; + } + } + return true; +} diff --git a/src/mod_converter/model.cpp b/src/mod_converter/model.cpp index ac8cbbb..3b7dc6f 100644 --- a/src/mod_converter/model.cpp +++ b/src/mod_converter/model.cpp @@ -139,22 +139,28 @@ std::string block::printMtl(const std::string &mtl_name) const // d 1.0 // illum s += "\n"; - s += "map_Ka " + string(tex_mask) + ".tga\n"; - s += "map_Kd " + string(tex_mask) + ".tga\n"; - s += "map_Ks " + string(tex_spec) + ".tga\n"; - s += "map_Ns " + string(tex_spec) + ".tga\n"; + if (string(tex_mask) != "_DEFAULT_") + s += "map_Ka " + string(tex_mask) + ".tga" + "\n"; + if (string(tex_mask) != "_DEFAULT_") + s += "map_Kd " + string(tex_mask) + ".tga" + "\n"; + if (string(tex_spec) != "_DEFAULT_") + s += "map_Ks " + string(tex_spec) + ".tga" + "\n"; + if (string(tex_spec) != "_DEFAULT_") + s += "map_Ns " + string(tex_spec) + ".tga" + "\n"; s += "\n"; return s; } -std::string block::printObj() const +std::string block::printObj(const std::string &mtl_name) const { string s; - s += string("o ") + name + "\n"; - s += string("g ") + name + "\n"; + // UE does not recognize russian strings in .obj + //s += string("o ") + name + "\n"; + //s += string("g ") + name + "\n"; + s += "g group1\n"; s += "s off\n"; s += "\n"; - s += "usemtl main\n"; + s += "usemtl " + mtl_name + "\n"; s += "\n"; for (auto &v : vertices) @@ -201,12 +207,12 @@ void block::load(buffer &b) READ(b, unk4); if (size == 0) // critical error!!! cannot survive - throw std::runtime_error("model file has bad block size field"); + throw std::runtime_error("model file has bad block size field (size == 0)"); // data buffer data = buffer(b, size); - // we cannot process this type at the moment + // we cannot process this type at the moment if (type == BlockType::ParticleEmitter) return; @@ -253,7 +259,7 @@ void block::load(buffer &b) { // unknown end of block auto triangles2 = triangles; - triangles2.resize((data.getSize() - data.getIndex()) / sizeof(triangle)); + triangles2.resize((data.size() - data.index()) / sizeof(triangle)); for (auto &t : triangles2) READ(data, t); } @@ -271,29 +277,3 @@ void model::load(buffer &b) for (auto &f : blocks) f.load(b); } - -void model::writeObj(std::string fn) -{ - for (auto &f : blocks) - { - ofstream o(fn + "." + f.name + ".obj"); - o << "#" << "\n"; - o << "# A.I.M. Model Converter (ver. " << version() << ")\n"; - o << "#" << "\n"; - o << "\n"; - int p1 = fn.rfind("\\"); - int p2 = fn.rfind("/"); - auto mtl = fn.substr(std::max(p1, p2) + 1); - mtl += string(".") + f.name; - o << "mtllib " << mtl << ".mtl\n"; - o << "\n"; - o << f.printObj(); - - ofstream m(fn + "." + f.name + ".mtl"); - m << "#" << "\n"; - m << "# A.I.M. Model Converter (ver. " << version() << ")\n"; - m << "#" << "\n"; - m << "\n"; - m << f.printMtl(mtl); - } -} diff --git a/src/mod_converter/model.h b/src/mod_converter/model.h index e629176..53ae1a7 100644 --- a/src/mod_converter/model.h +++ b/src/mod_converter/model.h @@ -214,7 +214,7 @@ struct block void load(buffer &b); std::string printMtl(const std::string &mtl_name) const; - std::string printObj() const; + std::string printObj(const std::string &mtl_name) const; }; struct model @@ -224,5 +224,4 @@ struct model std::vector blocks; void load(buffer &b); - void writeObj(std::string fn); }; diff --git a/src/obj_extractor/obj_extractor.bat b/src/obj_extractor/obj_extractor.bat new file mode 100644 index 0000000..5d95771 --- /dev/null +++ b/src/obj_extractor/obj_extractor.bat @@ -0,0 +1 @@ +python obj_extractor.py --db "h:\Games\Epic Games\Projects\Polygon4\Mods\db.sqlite" --dir "h:\Games\AIM\data\maps.pak.dir" \ No newline at end of file diff --git a/src/obj_extractor/obj_extractor.cpp b/src/obj_extractor/obj_extractor.cpp index a00b9ff..1817e8c 100644 --- a/src/obj_extractor/obj_extractor.cpp +++ b/src/obj_extractor/obj_extractor.cpp @@ -55,7 +55,7 @@ struct storage if (!b.eof()) { stringstream ss; - ss << hex << b.getIndex() << " != " << hex << b.getSize(); + ss << hex << b.index() << " != " << hex << b.size(); throw std::logic_error(ss.str()); } } @@ -99,7 +99,10 @@ void write_mmo(string db, const storage &s) } if (map_id == 0) + { + printf("error: this map is not found in the database\n"); return; + } auto this_map = storage->maps[map_id]; @@ -122,10 +125,10 @@ void write_mmo(string db, const storage &s) { auto bld = storage->addBuilding(); bld->text_id = o; - bld_ids[o] = bld->id; + bld_ids[o] = bld->getId(); } else - bld_ids[o] = iter->second->id; + bld_ids[o] = iter->second->getId(); } for (auto &object : segment->objects) { @@ -146,7 +149,7 @@ void write_mmo(string db, const storage &s) if (i == storage->mapBuildings.end()) { auto mb2 = storage->addMapBuilding(storage->maps[map_id].get()); - mb.id = mb2->id; + mb.setId(mb2->getId()); *mb2.get() = mb; } } @@ -170,10 +173,10 @@ void write_mmo(string db, const storage &s) { auto bld = storage->addObject(); bld->text_id = o; - bld_ids[o] = bld->id; + bld_ids[o] = bld->getId(); } else - bld_ids[o] = iter->second->id; + bld_ids[o] = iter->second->getId(); } for (auto &object : segment->objects) { @@ -194,7 +197,7 @@ void write_mmo(string db, const storage &s) if (i == storage->mapObjects.end()) { auto mb2 = storage->addMapObject(storage->maps[map_id].get()); - mb.id = mb2->id; + mb.setId(mb2->getId()); *mb2.get() = mb; } } diff --git a/src/obj_extractor/obj_extractor.py b/src/obj_extractor/obj_extractor.py new file mode 100644 index 0000000..301b751 --- /dev/null +++ b/src/obj_extractor/obj_extractor.py @@ -0,0 +1,26 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +import argparse +import os +import subprocess + +def main(): + parser = argparse.ArgumentParser(description='Batch models converter') + parser.add_argument('--dir', dest='dir', help='path to directory with maps') + parser.add_argument('--db', dest='db', help='path to db') + pargs = parser.parse_args() + + if pargs.dir: + run(pargs.dir, pargs.db) + +def run(dir, db): + for file in sorted(os.listdir(dir)): + if os.path.isdir(file) or os.path.splitext(file)[1] != ".mmo": + continue + print('loading: ' + file) + p = subprocess.Popen(['obj_extractor.exe', db, dir + '/' + file]) + p.communicate() + +if __name__ == '__main__': + main() diff --git a/src/obj_extractor/objects.h b/src/obj_extractor/objects.h index 8ace369..fa24e1c 100644 --- a/src/obj_extractor/objects.h +++ b/src/obj_extractor/objects.h @@ -171,7 +171,7 @@ KNOWN_OBJECT(Anomaly); KNOWN_OBJECT(Boundary); #define UNKNOWN_OBJECT(name) \ - struct name : public MapObject { void load(buffer &b){ int pos = b.getIndex(); assert(false); } } + struct name : public MapObject { void load(buffer &b){ int pos = b.index(); assert(false); } } UNKNOWN_OBJECT(Building); UNKNOWN_OBJECT(Goods); diff --git a/src/tm_converter/tm_converter.bat b/src/tm_converter/tm_converter.bat new file mode 100644 index 0000000..be0c4ee --- /dev/null +++ b/src/tm_converter/tm_converter.bat @@ -0,0 +1 @@ +python tm_converter.py --dir "h:\\Games\\Epic Games\\Projects\\AIM\\models\\aim1\\" \ No newline at end of file diff --git a/src/tm_converter/tm_converter.cpp b/src/tm_converter/tm_converter.cpp index a1403cb..db8d2d2 100644 --- a/src/tm_converter/tm_converter.cpp +++ b/src/tm_converter/tm_converter.cpp @@ -27,15 +27,31 @@ using namespace std; +void convert_simple(buffer &dst, buffer &src, int width, int height) +{ + int size = width * height * 2; + for (int i = 0; i < size; i++) + { + uint8_t c; + READ(src, c); + uint8_t lo = c & 0x0F; + uint8_t hi = (c & 0xF0) >> 4; + dst.write(uint8_t((lo << 4) | lo)); + dst.write(uint8_t((hi << 4) | hi)); + } +} + void tm2tga(string fn) { int width, height; + int dxt5 = 0; buffer src(readFile(fn)); READ(src, width); READ(src, height); - src.reset(); - src.skip(0x4C); + src.seek(0x10); + src.read(&dxt5, 1); + src.seek(0x4C); // http://paulbourke.net/dataformats/tga/ buffer dst; @@ -53,22 +69,19 @@ void tm2tga(string fn) dst.write(uint8_t(0x28)); // imagedescriptor const char *label = "AIMTMConverter"; - dst.write(label, strlen(label), false); + dst.write(label, strlen(label)); - int size = width * height * 2; - for (int i = 0; i < size; i++) + if (dxt5) { - uint8_t c; - READ(src, c); - uint8_t lo = c & 0x0F; - uint8_t hi = (c & 0xF0) >> 4; - dst.write(uint8_t((lo << 4) | lo)); - dst.write(uint8_t((hi << 4) | hi)); + //convert_dxt5(dst, src, width, height); + throw std::logic_error("dxt5 converter is not implemented!"); } + else + convert_simple(dst, src, width, height); transform(fn.begin(), fn.end(), fn.begin(), ::tolower); fn = fn.substr(0, fn.rfind(".tm")) + ".tga"; - writeFile(fn, dst.getBuf()); + writeFile(fn, dst.buf()); } int main(int argc, char *argv[]) diff --git a/src/tm_converter/tm_converter.py b/src/tm_converter/tm_converter.py new file mode 100644 index 0000000..97921d4 --- /dev/null +++ b/src/tm_converter/tm_converter.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +import argparse +import os +import subprocess + +def main(): + parser = argparse.ArgumentParser(description='Batch textures converter') + parser.add_argument('--dir', dest='dir', help='path to directory with textures') + pargs = parser.parse_args() + + if pargs.dir: + run(pargs.dir) + +def run(dir): + for file in sorted(os.listdir(dir)): + if os.path.isdir(file) or os.path.splitext(file)[1].lower() != ".tm": + continue + p = subprocess.Popen(['tm_converter.exe', dir + '/' + file]) + p.communicate() + +if __name__ == '__main__': + main()