/* Copyright (C) 2012 Sebastian Herbord. All rights reserved. This file is part of Mod Organizer. Mod Organizer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Mod Organizer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ #include "dummybsa.h" #include #include #define WIN32_LEAN_AND_MEAN #include static void writeUlong(unsigned char* buffer, int offset, unsigned long value) { union { unsigned long ulValue; unsigned char cValue[4]; }; ulValue = value; memcpy(buffer + offset, cValue, 4); } static void writeUlonglong(unsigned char* buffer, int offset, unsigned long long value) { union { unsigned long long ullValue; unsigned char cValue[8]; }; ullValue = value; memcpy(buffer + offset, cValue, 8); } /* static unsigned long long genHashInt(const char* pos, const char* end) { unsigned long long hash2 = 0; for (; pos < end; ++pos) { hash2 *= 0x1003f; hash2 += *pos; } return hash2; } static unsigned long long genHash(const char* fileName) { char fileNameLower[MAX_PATH + 1]; int i = 0; for (; i < MAX_PATH && fileName[i] != '\0'; ++i) { fileNameLower[i] = tolower(fileName[i]); } fileNameLower[i] = '\0'; char* ext = strrchr(fileNameLower, '.'); if (ext == NULL) { ext = fileNameLower + strlen(fileNameLower); } int length = ext - fileNameLower; unsigned long long hash = 0ULL; if (length > 0) { hash = *(ext - 1) | ((length > 2 ? *(ext - 2) : 0) << 8) | (length << 16) | (fileNameLower[0] << 24); } if (strcmp(ext + 1, "dds") == 0) { hash |= 0x8080; } // the rest is not supported currently hash |= genHashInt(fileNameLower + 1, ext - 2) << 0x32; hash |= genHashInt(ext, ext + strlen(ext)) << 0x32; return hash; }*/ static unsigned long genHashInt(const unsigned char *pos, const unsigned char *end) { unsigned long hash = 0; for (; pos < end; ++pos) { hash *= 0x1003f; hash += *pos; } return hash; } static unsigned long long genHash(const char* fileName) { char fileNameLower[MAX_PATH + 1]; int i = 0; for (; i < MAX_PATH && fileName[i] != '\0'; ++i) { fileNameLower[i] = static_cast(tolower(fileName[i])); if (fileNameLower[i] == '\\') { fileNameLower[i] = '/'; } } fileNameLower[i] = '\0'; unsigned char *fileNameLowerU = reinterpret_cast(fileNameLower); char* ext = strrchr(fileNameLower, '.'); if (ext == NULL) { ext = fileNameLower + strlen(fileNameLower); } unsigned char *extU = reinterpret_cast(ext); int length = ext - fileNameLower; unsigned long long hash = 0ULL; if (length > 0) { hash = *(extU - 1) | ((length > 2 ? *(ext - 2) : 0) << 8) | (length << 16) | (fileNameLowerU[0] << 24); } if (strlen(ext) > 0) { if (strcmp(ext + 1, "kf") == 0) { hash |= 0x80; } else if (strcmp(ext + 1, "nif") == 0) { hash |= 0x8000; } else if (strcmp(ext + 1, "dds") == 0) { hash |= 0x8080; } else if (strcmp(ext + 1, "wav") == 0) { hash |= 0x80000000; } unsigned long long temp = static_cast(genHashInt( fileNameLowerU + 1, extU - 2)); temp += static_cast(genHashInt( extU, extU + strlen(ext))); hash |= (temp & 0xFFFFFFFF) << 32; } return hash; } DummyBSA::DummyBSA() : m_Version(MOShared::GameInfo::instance().getBSAVersion()), m_FolderName(""), m_FileName("dummy.dds"), m_TotalFileNameLength(0) { } void DummyBSA::writeHeader(QFile& file) { unsigned char header[] = { 'B', 'S', 'A', '\0', // magic string 0xDE, 0xAD, 0xBE, 0xEF, // version - insert later 0x24, 0x00, 0x00, 0x00, // offset to folder recors. header size is static 0xDE, 0xAD, 0xBE, 0xEF, // archive flags - insert later 0x01, 0x00, 0x00, 0x00, // folder count 0x01, 0x00, 0x00, 0x00, // file count 0xDE, 0xAD, 0xBE, 0xEF, // total folder names length - insert later 0xDE, 0xAD, 0xBE, 0xEF, // total file names length - insert later 0xDE, 0xAD, 0xBE, 0xEF // file flags - insert later }; writeUlong(header, 4, MOShared::GameInfo::instance().getBSAVersion()); writeUlong(header, 12, 0x01 | 0x02); // has directories and has files. writeUlong(header, 24, m_FolderName.length() + 1); // empty folder name writeUlong(header, 28, m_TotalFileNameLength); // single character file name writeUlong(header, 32, 2); // has dds file.write(reinterpret_cast(header), sizeof(header)); } void DummyBSA::writeFolderRecord(QFile& file, const std::string& folderName) { unsigned char folderRecord[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // folder hash 0x01, 0x00, 0x00, 0x00, // file count 0xDE, 0xAD, 0xBE, 0xEF, // offset to folder name }; // we'd usually have to sort folders be the hash value generated here writeUlonglong(folderRecord, 0, genHash(folderName.c_str())); writeUlong( folderRecord, 12, 0x34 + m_TotalFileNameLength); // TODO: this should be calculated properly file.write(reinterpret_cast(folderRecord), sizeof(folderRecord)); } void DummyBSA::writeFileRecord(QFile& file, const std::string& fileName) { unsigned char fileRecord[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // file name hash 0xDE, 0xAD, 0xBE, 0xEF, // size 0xDE, 0xAD, 0xBE, 0xEF, // offset to file data }; // we'd usually have to sort files by the value generated here writeUlonglong(fileRecord, 0, genHash(fileName.c_str())); writeUlong( fileRecord, 8, 0); writeUlong( fileRecord, 12, 0x44 + (fileName.length() + 1) + 4); // after this record we expect the filename and 4 bytes of file size file.write(reinterpret_cast(fileRecord), sizeof(fileRecord)); } void DummyBSA::writeFileRecordBlocks(QFile& file, const std::string& folderName) { file.write(folderName.c_str(), folderName.length() + 1); writeFileRecord(file, m_FileName); } void DummyBSA::write(const QString& fileName) { QFile file(fileName); file.open(QIODevice::WriteOnly); m_TotalFileNameLength = m_FileName.length() + 1; writeHeader(file); writeFolderRecord(file, m_FolderName); writeFileRecordBlocks(file, m_FolderName); file.write(m_FileName.c_str() , m_FileName.length() + 1); char fileSize[] = { 0x00, 0x00, 0x00, 0x00 }; file.write(fileSize, sizeof(fileSize)); file.close(); }