From 7b3e9c449e711443e2779a899ea77c700c360358 Mon Sep 17 00:00:00 2001 From: Stefanos Kornilios Mitsis Poiitidis Date: Wed, 5 Mar 2025 23:04:02 +0200 Subject: [PATCH] repack col files, remove use of (unused) peds.col --- dreamcast/Makefile | 35 +++++-- dreamcast/coltool.cpp | 157 +++++++++++++++++++++++++++++ dreamcast/gta3files.mk | 3 +- dreamcast/imgmisc.mk | 2 +- miami/collision/ColTriangle.h | 2 +- miami/collision/CompressedVector.h | 1 + miami/core/FileLoader.cpp | 17 ++-- miami/core/main.cpp | 50 ++++----- 8 files changed, 224 insertions(+), 43 deletions(-) create mode 100644 dreamcast/coltool.cpp diff --git a/dreamcast/Makefile b/dreamcast/Makefile index 1bd3826d..ea0cba10 100644 --- a/dreamcast/Makefile +++ b/dreamcast/Makefile @@ -296,6 +296,9 @@ texconv: $(OBJS_TEXCONV) | pvrtex # You'll have to rebuild pvrtex manually if yo animtool: animtool.cpp $(CXX) -std=c++17 -o $@ -g -O0 $< +coltool: coltool.cpp + $(CXX) -std=c++17 -o $@ -g -O0 $< + -include $(DEPS) #### Repacking #### @@ -351,7 +354,7 @@ TEXTURE_DOWNSAMPLE_IMG ?= HALF IMG_TEXTURES_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_TEXTURES)) IMG_MODELS_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_MODELS)) IMG_IFP_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_IFP)) -IMG_MISC_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_MISC)) +IMG_COL_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_COL)) CUTS_IFP_DC = $(addprefix $(REPACK_CUTS_DC_DIR)/, $(CUTS_IFP)) CUTS_MISC_DC = $(addprefix $(REPACK_CUTS_DC_DIR)/, $(CUTS_MISC)) @@ -368,7 +371,7 @@ STREAM_ADPCM_DC = $(addprefix $(REPACK_GTA_DIR)/stream/, $(STREAM_WAV:.wav=.APM) IMG_TEXTURES_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_TEXTURES)) IMG_MODELS_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_MODELS)) IMG_IFP_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_IFP)) -IMG_MISC_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_MISC)) +IMG_COL_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_COL)) CUTS_IFP_ORIG = $(addprefix $(REPACK_CUTS_ORIG_DIR)/, $(CUTS_IFP)) CUTS_MISC_ORIG = $(addprefix $(REPACK_CUTS_ORIG_DIR)/, $(CUTS_MISC)) @@ -392,7 +395,7 @@ $(REPACK_DIR)/unpacked: imgtool $(GTA_DIR)/models/gta3.img $(GTA_DIR)/models/gta ./imgtool unpack "$(GTA_DIR)/models/gta3" "$(REPACK_IMG_ORIG_DIR)" @touch $@ -$(IMG_TEXTURES_ORIG) $(IMG_MODELS_ORIG) $(IMG_IFP_ORIG) $(IMG_MISC_ORIG): $(REPACK_DIR)/unpacked +$(IMG_TEXTURES_ORIG) $(IMG_MODELS_ORIG) $(IMG_IFP_ORIG) $(IMG_COL_ORIG): $(REPACK_DIR)/unpacked @test -f $@ @touch $@ @@ -438,17 +441,17 @@ $(REPACK_IMG_DC_DIR)/%.TXD: $(REPACK_IMG_ORIG_DIR)/%.TXD texconv ./texconv $< $@ $(DEFAULT_RES) $(DEFAULT_RES) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_IMG) # First try the mods img directory -$(REPACK_IMG_DC_DIR)/%.col: $(GTA_MOD_IMG_DIR)/%.col +$(REPACK_IMG_DC_DIR)/%.col: $(GTA_MOD_IMG_DIR)/%.col coltool @mkdir -p $(@D) - cp $< $@ + ./coltool $< $@ $(REPACK_IMG_DC_DIR)/%.ifp: $(GTA_MOD_IMG_DIR)/%.ifp animtool @mkdir -p $(@D) ./animtool $< $@ # if not, the extracted img directory -$(REPACK_IMG_DC_DIR)/%.col: $(REPACK_IMG_ORIG_DIR)/%.col +$(REPACK_IMG_DC_DIR)/%.col: $(REPACK_IMG_ORIG_DIR)/%.col coltool @mkdir -p $(@D) - cp $< $@ + ./coltool $< $@ $(REPACK_IMG_DC_DIR)/%.ifp: $(REPACK_IMG_ORIG_DIR)/%.ifp animtool @mkdir -p $(@D) ./animtool $< $@ @@ -520,7 +523,23 @@ $(REPACK_GTA_DIR)/%.IFP: $(GTA_DIR)/%.IFP animtool @mkdir -p $(@D) ./animtool $< $@ -$(REPACK_DIR)/packed: $(IMG_TEXTURES_DC) $(IMG_MODELS_DC) $(IMG_IFP_DC) $(IMG_MISC_DC) +# first try the mods loose directory +$(REPACK_GTA_DIR)/%.col: $(GTA_MOD_LOOSE_DIR)/%.col coltool + @mkdir -p $(@D) + ./coltool $< $@ +$(REPACK_GTA_DIR)/%.COL: $(GTA_MOD_LOOSE_DIR)/%.COL coltool + @mkdir -p $(@D) + ./coltool $< $@ + +#if not, the original files +$(REPACK_GTA_DIR)/%.col: $(GTA_DIR)/%.col coltool + @mkdir -p $(@D) + ./coltool $< $@ +$(REPACK_GTA_DIR)/%.COL: $(GTA_DIR)/%.COL coltool + @mkdir -p $(@D) + ./coltool $< $@ + +$(REPACK_DIR)/packed: $(IMG_TEXTURES_DC) $(IMG_MODELS_DC) $(IMG_IFP_DC) $(IMG_COL_DC) mkdir -p $(@D) mkdir -p "$(REPACK_GTA_DIR)/models/gta3" ./imgtool pack "$(REPACK_GTA_DIR)/models/gta3" "$(REPACK_IMG_DC_DIR)" diff --git a/dreamcast/coltool.cpp b/dreamcast/coltool.cpp new file mode 100644 index 00000000..4609ba7d --- /dev/null +++ b/dreamcast/coltool.cpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include +#include + +// Our collision file header. In the original code the header is read from the file. +// Here we assume it contains an identifier and a size. +struct ColHeader { + int32_t ident; // should be 'LLOC' + int32_t size; // size of the following collision data chunk +}; + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: repack_collision \n"; + return 1; + } + const char* inputFile = argv[1]; + const char* outputFile = argv[2]; + + std::ifstream infile(inputFile, std::ios::binary); + if (!infile) { + std::cerr << "Error: Cannot open input file: " << inputFile << "\n"; + return 1; + } + std::ofstream outfile(outputFile, std::ios::binary); + if (!outfile) { + std::cerr << "Error: Cannot open output file: " << outputFile << "\n"; + return 1; + } + + // Process each collision model in the file. + // Each chunk starts with a ColHeader followed by header.size bytes. + while (infile.peek() != EOF) { + ColHeader header; + infile.read(reinterpret_cast(&header), sizeof(header)); + if (!infile) break; + + // Check that the header ident is correct (should equal 'LLOC') + if (header.ident != 'LLOC') { + auto curpos = infile.tellg(); + infile.seekg(0, infile.end); + auto remaining = infile.tellg() - curpos; + + if (remaining < 2048) { + return 0; + } else { + std::cerr << "Error: Invalid header identifier encountered.\n"; + return 1; + } + } + + header.ident = 'CLOC'; + + // Read the collision chunk into a work buffer. + std::vector work_buff(header.size); + infile.read(work_buff.data(), header.size); + if (!infile) break; + + // The first 24 bytes of work_buff are the model name. + // The rest of the buffer is the collision model data. + // We now “repackage” the collision model data into a new vector. + std::vector out_buff; + // Copy model name (first 24 bytes) + out_buff.insert(out_buff.end(), work_buff.begin(), work_buff.begin() + 24); + + // Set an offset into work_buff where the collision model data begins. + size_t offset = 24; + + // === Process Collision Model Data === + // Copy first 44 bytes (bounding sphere, bounding box, and extra bytes) + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 44); + // We need to extract numSpheres which is stored at offset 40 (within collision data) + int16_t numSpheres = *reinterpret_cast(&work_buff[24 + 40]); + offset += 44; + + assert(work_buff.size() > offset); + // Copy each sphere data block (20 bytes per sphere) + for (int i = 0; i < numSpheres; i++) { + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 20); + offset += 20; + } + + // Process lines: read 4 bytes (includes numLines) + int16_t numLines = *reinterpret_cast(&work_buff[offset]); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 4); + offset += 4; + // For each line, copy 24 bytes + for (int i = 0; i < numLines; i++) { + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 24); + offset += 24; + } + + // Process boxes: next 4 bytes hold numBoxes + int16_t numBoxes = *reinterpret_cast(&work_buff[offset]); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 4); + offset += 4; + // For each box, copy 28 bytes + for (int i = 0; i < numBoxes; i++) { + assert(work_buff.size() > offset); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 28); + offset += 28; + } + + // Process vertices: next 4 bytes (numVertices) + int16_t numVertices = *reinterpret_cast(&work_buff[offset]); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 4); + offset += 4; + assert(work_buff.size() > offset); + + // For each vertex, convert to fixed point (6 bytes for every 12 bytes in) + for (int i = 0; i < numVertices; i++) { + for (int v = 0; v < 3; v++) { + assert(work_buff.size() > offset); + float d = *(float*)(work_buff.data() + offset); + offset += 4; + auto fix = llround(d * 128); + assert(fix >= -32768 && fix <32767); + int16_t fix16 = int16_t(fix); + out_buff.insert(out_buff.end(), (uint8_t*)(&fix16), (uint8_t*)(&fix16 + 1)); + } + } + + // Process triangles: next 4 bytes (numTriangles) + int16_t numTriangles = *reinterpret_cast(&work_buff[offset]); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 4); + offset += 4; + // For each triangle, copy 16 bytes + for (int i = 0; i < numTriangles; i++) { + for (int v = 0; v < 3; v++) { + int32_t d = *(int32_t*)(work_buff.data() + offset); + offset += 4; + assert(d >= -32768 && d <32767); + assert(d < numVertices); + int16_t d16 = int16_t(d); + out_buff.insert(out_buff.end(), (uint8_t*)(&d16), (uint8_t*)(&d16 + 1)); + } + + out_buff.push_back(work_buff[offset]); + out_buff.push_back(0); // padding + offset += 4; + } + // === End repackaging of collision model data === + + // Update header.size to match our repackaged chunk size. + header.size = static_cast(out_buff.size()); + + // Write the header and the repackaged collision model data to the output file. + outfile.write(reinterpret_cast(&header), sizeof(header)); + outfile.write(out_buff.data(), out_buff.size()); + } + + return 0; +} diff --git a/dreamcast/gta3files.mk b/dreamcast/gta3files.mk index fb327b06..246145d0 100644 --- a/dreamcast/gta3files.mk +++ b/dreamcast/gta3files.mk @@ -1,3 +1,5 @@ +# models/coll/peds.col # seems like this is corrupted + MISC_FILES = \ \ txd/LOADSC8.TXD \ @@ -27,7 +29,6 @@ MISC_FILES = \ models/fonts.txd \ models/hud.txd \ models/coll/weapons.col \ - models/coll/peds.col \ models/coll/vehicles.col \ models/coll/generic.col \ models/MISC.TXD \ diff --git a/dreamcast/imgmisc.mk b/dreamcast/imgmisc.mk index a29b0bd2..948a8e4f 100644 --- a/dreamcast/imgmisc.mk +++ b/dreamcast/imgmisc.mk @@ -1,4 +1,4 @@ -IMG_MISC = \ +IMG_COL = \ airport.col \ airportN.col \ bank.col \ diff --git a/miami/collision/ColTriangle.h b/miami/collision/ColTriangle.h index a2580c58..5af0cf76 100644 --- a/miami/collision/ColTriangle.h +++ b/miami/collision/ColTriangle.h @@ -18,7 +18,7 @@ struct CColTriangle uint16 c; uint8 surface; - void Set(int a, int b, int c, uint8 surf) + void Set(uint16 a, uint16 b, uint16 c, uint8 surf) { this->a = a; this->b = b; diff --git a/miami/collision/CompressedVector.h b/miami/collision/CompressedVector.h index d54e49b1..db6c0560 100644 --- a/miami/collision/CompressedVector.h +++ b/miami/collision/CompressedVector.h @@ -5,6 +5,7 @@ struct CompressedVector #ifdef COMPRESSED_COL_VECTORS int16 x, y, z; CVector Get(void) const { return CVector(x, y, z)/128.0f; }; + void SetFixed(int16 x, int16 y, int16 z) { this->x = x; this->y = y; this->z = z; }; void Set(float x, float y, float z) { this->x = x*128.0f; this->y = y*128.0f; this->z = z*128.0f; }; #ifdef GTA_PS2 void Unpack(uint128 &qword) const { diff --git a/miami/core/FileLoader.cpp b/miami/core/FileLoader.cpp index b215673a..3a6b80c5 100644 --- a/miami/core/FileLoader.cpp +++ b/miami/core/FileLoader.cpp @@ -189,7 +189,7 @@ CFileLoader::LoadCollisionFile(const char *filename, uint8 colSlot) assert(fd > 0); while(CFileMgr::Read(fd, (char*)&header, sizeof(header))){ - assert(header.ident == 'LLOC'); + assert(header.ident == 'CLOC'); CFileMgr::Read(fd, (char*)work_buff, header.size); memcpy(modelname, work_buff, 24); @@ -226,7 +226,7 @@ CFileLoader::LoadCollisionFileFirstTime(uint8 *buffer, uint32 size, uint8 colSlo while(size > 8){ header = (ColHeader*)buffer; modelsize = header->size; - if(header->ident != 'LLOC') + if(header->ident != 'CLOC') return size-8 < CDSTREAM_SECTOR_SIZE; memcpy(modelname, buffer+8, 24); memcpy(work_buff, buffer+32, modelsize-24); @@ -260,7 +260,7 @@ CFileLoader::LoadCollisionFile(uint8 *buffer, uint32 size, uint8 colSlot) while(size > 8){ header = (ColHeader*)buffer; modelsize = header->size; - if(header->ident != 'LLOC') + if(header->ident != 'CLOC') return size-8 < CDSTREAM_SECTOR_SIZE; memcpy(modelname, buffer+8, 24); memcpy(work_buff, buffer+32, modelsize-24); @@ -345,14 +345,14 @@ CFileLoader::LoadCollisionModel(uint8 *buf, CColModel &model, char *modelname) model.vertices = (CompressedVector*)RwMalloc(numVertices*sizeof(CompressedVector)); REGISTER_MEMPTR(&model.vertices); for(i = 0; i < numVertices; i++){ - model.vertices[i].Set(*(float*)buf, *(float*)(buf+4), *(float*)(buf+8)); + model.vertices[i].SetFixed(*(int16*)buf, *(int16*)(buf+2), *(int16*)(buf+4)); #if 0 if(Abs(*(float*)buf) >= 256.0f || Abs(*(float*)(buf+4)) >= 256.0f || Abs(*(float*)(buf+8)) >= 256.0f) printf("%s:Collision volume too big\n", modelname); #endif - buf += 12; + buf += 6; } }else model.vertices = nil; @@ -363,8 +363,11 @@ CFileLoader::LoadCollisionModel(uint8 *buf, CColModel &model, char *modelname) model.triangles = (CColTriangle*)RwMalloc(model.numTriangles*sizeof(CColTriangle)); REGISTER_MEMPTR(&model.triangles); for(i = 0; i < model.numTriangles; i++){ - model.triangles[i].Set(*(int32*)buf, *(int32*)(buf+4), *(int32*)(buf+8), buf[12]); - buf += 16; + model.triangles[i].Set(*(uint16*)buf, *(uint16*)(buf+2), *(uint16*)(buf+4), buf[6]); + buf += 8; + assert(model.triangles[i].a < numVertices); + assert(model.triangles[i].b < numVertices); + assert(model.triangles[i].c < numVertices); } }else model.triangles = nil; diff --git a/miami/core/main.cpp b/miami/core/main.cpp index ef808623..9d63f8ee 100644 --- a/miami/core/main.cpp +++ b/miami/core/main.cpp @@ -169,36 +169,36 @@ Error(char *fmt, ...) void ValidateVersion() { - int32 file = CFileMgr::OpenFile("models\\coll\\peds.col", "rb"); - char buff[128]; + // int32 file = CFileMgr::OpenFile("models\\coll\\peds.col", "rb"); + // char buff[128]; - if ( file != -1 ) - { - CFileMgr::Seek(file, 100, SEEK_SET); + // if ( file != -1 ) + // { + // CFileMgr::Seek(file, 100, SEEK_SET); - for ( int i = 0; i < 128; i++ ) - { - CFileMgr::Read(file, &buff[i], sizeof(char)); - buff[i] -= 23; - if ( buff[i] == '\0' ) - break; - CFileMgr::Seek(file, 99, SEEK_CUR); - } + // for ( int i = 0; i < 128; i++ ) + // { + // CFileMgr::Read(file, &buff[i], sizeof(char)); + // buff[i] -= 23; + // if ( buff[i] == '\0' ) + // break; + // CFileMgr::Seek(file, 99, SEEK_CUR); + // } - if ( !strncmp(buff, "grandtheftauto3", 15) ) - { - strncpy(version_name, &buff[15], 64); - CFileMgr::CloseFile(file); - return; - } - } + // if ( !strncmp(buff, "grandtheftauto3", 15) ) + // { + // strncpy(version_name, &buff[15], 64); + // CFileMgr::CloseFile(file); + // return; + // } + // } - LoadingScreen("Invalid version", NULL, NULL); + // LoadingScreen("Invalid version", NULL, NULL); - while(true) - { - ; - } + // while(true) + // { + // ; + // } } bool