diff --git a/CMakeLists.txt b/CMakeLists.txt index a55bd29c..fe168775 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + project(gtfw) SET(CMAKE_CXX_FLAGS "-g -std=c++11") add_subdirectory(datadump) add_subdirectory(viewer) + +add_subdirectory(framework2) +add_subdirectory(analyzer) diff --git a/analyzer/CMakeLists.txt b/analyzer/CMakeLists.txt new file mode 100644 index 00000000..f204832c --- /dev/null +++ b/analyzer/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(analyzer main.cpp) + +include_directories(../framework2/include) + +target_link_libraries( analyzer renderware sfml-graphics ) + +install(TARGETS analyzer RUNTIME DESTINATION bin) diff --git a/analyzer/main.cpp b/analyzer/main.cpp new file mode 100644 index 00000000..da94b2ac --- /dev/null +++ b/analyzer/main.cpp @@ -0,0 +1,77 @@ +#include +#include + +#include + +#include +#include + +int main() +{ + std::cout << std::showbase; + + std::string gtapath = "/home/iostream/.wine/drive_c/Program Files (x86)/Rockstar Games/GTAIII/"; + auto ojg = RW::BinaryStream::parse(gtapath +"/models/MISC.TXD"); + // auto ojg = RW::BinaryStream::parse("OJG.TXD"); + + auto texture = RW::TextureArchive::create(*ojg); + + std::cout << "Found " << texture->numTextures << " textures!" << std::endl; + sf::Image img; + for (size_t i = 0; i < texture->numTextures; i++) { + auto &tex = texture->textures[i]; + std::cout << "Processing " << tex.header.diffuseName << std::endl; + + img.create(tex.header.width, tex.header.height); + + for (int j = 0; j < tex.header.width; j++) { + for (int i = 0; i < tex.header.width; i++) { + bool hasAlpha = (tex.header.rasterFormat & 0x0500) == 0x0500; + if (tex.header.rasterFormat & 0x2000) { + uint8_t pixelIndex = 4 * tex.body.pixels[j*tex.header.width + i]; + + img.setPixel(i, j, { + tex.body.palette[pixelIndex + 0], + tex.body.palette[pixelIndex + 1], + tex.body.palette[pixelIndex + 2], + (hasAlpha ? tex.body.palette[pixelIndex + 3] : static_cast(255)) + }); + } else { + uint8_t pixel = 4 * tex.body.pixels[j*tex.header.width + i]; + img.setPixel(i, j, { + tex.body.pixels[pixel + 0], + tex.body.pixels[pixel + 1], + tex.body.pixels[pixel + 2], + tex.body.pixels[pixel + 3], + }); + } + } + } + + std::stringstream ss; + ss << tex.header.diffuseName << ".png"; + img.saveToFile(ss.str()); + + size_t scaleUp = 8; + img.create(16*scaleUp, 16*scaleUp); + for (int j = 0; j < 16; j++) { + for (int i = 0; i < 16; i++) { + uint8_t pixelIndex = 4 * (j*16 + i); + + for (int y = 0; y < scaleUp; y++) { + for (int x = 0; x < scaleUp; x++) { + img.setPixel((i*scaleUp)+x, (j*scaleUp)+y, { + tex.body.palette[pixelIndex + 0], + tex.body.palette[pixelIndex + 1], + tex.body.palette[pixelIndex + 2], + tex.body.palette[pixelIndex + 3], + }); + } + } + } + } + img.saveToFile("palette"+ ss.str()); + } + + return 0; +} diff --git a/framework2/BinaryStream.cpp b/framework2/BinaryStream.cpp new file mode 100644 index 00000000..d35c831c --- /dev/null +++ b/framework2/BinaryStream.cpp @@ -0,0 +1,89 @@ +#include + +#include +#include +#include + +namespace RW +{ + +std::unique_ptr BinaryStream::parse(std::string filename) +{ + std::ifstream dfile(filename); + if ( ! dfile.is_open()) { + std::cerr << "Error opening file " << filename << std::endl; + return nullptr; + } + + dfile.seekg(0, std::ios_base::end); + size_t length = dfile.tellg(); + dfile.seekg(0); + char *data = new char[length]; + dfile.read(data, length); + // std::cout << "File is " << length << " bytes" << std::endl << std::endl; + + auto BS = std::unique_ptr(new BinaryStream); + + // Set file's ACTUAL length + auto header = reinterpret_cast(data); + length = header->size + sizeof(nativeSectionHeader_t); + + sectionHeader_t *prevHeader = nullptr; + + size_t offset = 0; + while (offset < length) { + nativeSectionHeader_t *sectionHeader = reinterpret_cast(data + offset); + sectionHeader_t *sec = new sectionHeader_t; + sec->ID = sectionHeader->ID; + sec->size = sectionHeader->size; + sec->version = sectionHeader->version; + if (prevHeader == nullptr) + BS->rootHeader = sec; + else + prevHeader->next = sec; + + if (sectionHeader->ID == 0) { + std::cout << "Section ID is ZERO! Abort!" << std::endl; + break; + } + + std::cout << "Section " << std::hex << sectionHeader->ID + << " (" << sectionIdString(sectionHeader->ID) << ")" + << " - " << std::dec << sectionHeader->size << " bytes" << std::endl; +/* + std::cout << "Offset " << std::hex << offset << std::endl; +*/ + + size_t bytesOfData = 0; + switch (sectionHeader->ID) { + case STRUCT: + bytesOfData = sectionHeader->size; + sec->data = new uint8_t[bytesOfData]; + memcpy(sec->data, data + offset + sizeof(nativeSectionHeader_t), bytesOfData); + break; + } + // std::cout << "It has " << std::dec << bytesOfData << " bytes of data!" << std::endl; + offset += sizeof(nativeSectionHeader_t) + bytesOfData; + + // std::cout << std::endl; + + prevHeader = sec; + } + + delete[] data; + + return BS; +} + +std::string BinaryStream::sectionIdString(uint32_t id) +{ + switch (id) { + case STRUCT: return "STRUCT"; + case EXTENSION: return "EXTENSION"; + case TEXTURE_NATIVE: return "TEXTURE_NATIVE"; + case TEXTURE_DICTIONARY: return "TEXTURE_DICTIONARY"; + default: return "UNKNOWN"; + } +} + +} diff --git a/framework2/CMakeLists.txt b/framework2/CMakeLists.txt new file mode 100644 index 00000000..b5ce486e --- /dev/null +++ b/framework2/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(renderware BinaryStream.cpp TextureArchive.cpp ) + +include_directories(include) diff --git a/framework2/TextureArchive.cpp b/framework2/TextureArchive.cpp new file mode 100644 index 00000000..fa1d35a6 --- /dev/null +++ b/framework2/TextureArchive.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include +#include + +namespace RW +{ + +std::unique_ptr TextureArchive::create(BinaryStream &binaryStream) +{ + auto textureArchive = std::unique_ptr(new TextureArchive); + + auto section = binaryStream.rootHeader; + + assert( + section->ID == BinaryStream::TEXTURE_DICTIONARY + && "BinaryStream passed to this function must be a TEXTURE DICTIONARY" + ); + + // Struct + section = section->next; + textureArchive->numTextures = ((uint16_t *) section->data)[0]; + + for (size_t i = 0; i < textureArchive->numTextures; i++) { + section = section->next; // Texture Native + section = section->next; // Struct + + Texture texture = *reinterpret_cast(section->data); + + if (texture.header.rasterFormat & 0x2000) { + memcpy(texture.body.palette, section->data + sizeof(TextureHeader), 1024); + + texture.body.pixels = new uint8_t[texture.header.width * texture.header.height]; + memcpy(texture.body.pixels, section->data + sizeof(TextureHeader) + 1024, texture.header.width * texture.header.height); + } else { + size_t bufSize = texture.header.width * texture.header.height * texture.header.bpp/8; + texture.body.pixels = new uint8_t[bufSize]; + memcpy(texture.body.pixels, section->data + sizeof(TextureHeader), bufSize); + } + + textureArchive->textures.push_back(texture); + + section = section->next; // Extension + } + + return textureArchive; +} + +} diff --git a/framework2/include/renderware/BinaryStream.hpp b/framework2/include/renderware/BinaryStream.hpp new file mode 100644 index 00000000..854c6685 --- /dev/null +++ b/framework2/include/renderware/BinaryStream.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace RW +{ + +class BinaryStream +{ +private: + struct nativeSectionHeader_t + { + uint32_t ID; + uint32_t size; + uint32_t version; + }; +public: + enum { + STRUCT = 0x0001, + EXTENSION = 0x0003, + TEXTURE_NATIVE = 0x0015, + TEXTURE_DICTIONARY = 0x0016, + }; + + struct sectionHeader_t { + uint32_t ID; + uint32_t size; + uint32_t version; + + uint8_t *data = nullptr; + sectionHeader_t *next = nullptr; + } *rootHeader; + + static std::unique_ptr parse(std::string filename); + + static std::string sectionIdString(uint32_t id); +}; + +} \ No newline at end of file diff --git a/framework2/include/renderware/TextureArchive.hpp b/framework2/include/renderware/TextureArchive.hpp new file mode 100644 index 00000000..195a2cf1 --- /dev/null +++ b/framework2/include/renderware/TextureArchive.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +namespace RW +{ + +class TextureArchive +{ +public: + struct TextureHeader { + uint32_t platformID; + uint16_t filterFlags; + uint8_t textureWrapV; + uint8_t textureWrapU; + uint8_t diffuseName[32]; + uint8_t alphaName[32]; + uint32_t rasterFormat; + uint32_t alpha; + uint16_t width; + uint16_t height; + uint8_t bpp; + uint8_t numMipmap; + uint8_t rasterType; + uint8_t compression; + uint32_t dataSize; + }; + struct TextureBody { + uint8_t palette[1024]; + uint8_t *pixels; + }; + struct Texture { + TextureHeader header; + TextureBody body; + }; + + + size_t numTextures; + std::vector textures; + + static std::unique_ptr create(BinaryStream &binaryStream); +}; + +} \ No newline at end of file