diff --git a/CMakeLists.txt b/CMakeLists.txt index 57f6b8a5b7..aaf269eefc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -610,7 +610,6 @@ add_subdirectory (extern/oics) if (BUILD_OPENCS) add_subdirectory (extern/osgQt) endif() -add_subdirectory (extern/BSAOpt) # Components add_subdirectory (components) diff --git a/components/bsa/compressedbsafile.cpp b/components/bsa/compressedbsafile.cpp index 40dbc28566..ba96ee8de9 100644 --- a/components/bsa/compressedbsafile.cpp +++ b/components/bsa/compressedbsafile.cpp @@ -28,12 +28,9 @@ #include #include -#include #include #include -#include // see: http://en.uesp.net/wiki/Tes4Mod:Hash_Calculation - #include #include #include @@ -197,8 +194,7 @@ void CompressedBSAFile::readHeader() if ((archiveFlags & 0x1) != 0) getBZString(folder, input); - std::string emptyString; - folderHash = GenOBHash(folder, emptyString); + folderHash = generateHash(folder, std::string()); std::map::iterator iter = mFolders.find(folderHash); if (iter == mFolders.end()) @@ -297,20 +293,13 @@ CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string p.remove_filename(); std::string folder = p.string(); - // GenOBHash already converts to lowercase and replaces file separators but not for path - boost::algorithm::to_lower(folder); - std::replace(folder.begin(), folder.end(), '/', '\\'); - - std::string emptyString; - std::uint64_t folderHash = GenOBHash(folder, emptyString); + std::uint64_t folderHash = generateHash(folder, std::string()); std::map::const_iterator it = mFolders.find(folderHash); if (it == mFolders.end()) return FileRecord(); // folder not found, return default which has offset of sInvalidOffset - boost::algorithm::to_lower(stem); - boost::algorithm::to_lower(ext); - std::uint64_t fileHash = GenOBHashPair(stem, ext); + std::uint64_t fileHash = generateHash(stem, ext); std::map::const_iterator iter = it->second.files.find(fileHash); if (iter == it->second.files.end()) return FileRecord(); // file not found, return default which has offset of sInvalidOffset @@ -430,4 +419,35 @@ void CompressedBSAFile::convertCompressedSizesToUncompressed() } } +std::uint64_t CompressedBSAFile::generateHash(std::string stem, std::string extension) const +{ + size_t len = stem.length(); + if (len == 0) return 0; + std::uint64_t hash = 0; + unsigned int hash2 = 0; + Misc::StringUtils::lowerCaseInPlace(stem); + if (extension.empty()) // It's a folder. + std::replace(stem.begin(), stem.end(), '/', '\\'); + else + { + Misc::StringUtils::lowerCaseInPlace(extension); + for (const char &c : extension) + hash = hash * 0x1003f + c; + } + for (size_t i = 1; i < len-2 && len > 3; i++) + hash2 = hash2 * 0x1003f + stem[i]; + hash = (hash + hash2) << 32; + hash2 = (stem[0] << 24) | (len << 16); + if (len >= 3) hash2 |= stem[len-2] << 8; + if (len >= 2) hash2 |= stem[len-1]; + if (!extension.empty()) + { + if (extension == ".kf") hash2 |= 0x80; + else if (extension == ".nif") hash2 |= 0x8000; + else if (extension == ".dds") hash2 |= 0x8080; + else if (extension == ".wav") hash2 |= 0x80000000; + } + return hash + hash2; +} + } //namespace Bsa diff --git a/components/bsa/compressedbsafile.hpp b/components/bsa/compressedbsafile.hpp index b607eb3eff..a22d6e149b 100644 --- a/components/bsa/compressedbsafile.hpp +++ b/components/bsa/compressedbsafile.hpp @@ -26,10 +26,6 @@ #ifndef BSA_COMPRESSED_BSA_FILE_H #define BSA_COMPRESSED_BSA_FILE_H -#include -#include -#include -#include #include namespace Bsa @@ -81,6 +77,8 @@ namespace Bsa void getBZString(std::string& str, std::istream& filestream); //mFiles used by OpenMW will contain uncompressed file sizes void convertCompressedSizesToUncompressed(); + /// \brief Normalizes given filename or folder and generates format-compatible hash. See https://en.uesp.net/wiki/Tes4Mod:Hash_Calculation. + std::uint64_t generateHash(std::string stem, std::string extension) const; Files::IStreamPtr getFile(const FileRecord& fileRecord); public: CompressedBSAFile(); diff --git a/extern/BSAOpt/CMakeLists.txt b/extern/BSAOpt/CMakeLists.txt deleted file mode 100644 index a7f1dcf74a..0000000000 --- a/extern/BSAOpt/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -# This is NOT intended as a stand-alone build system! Instead, you should include this from the main CMakeLists of your project. - -set(BSAOPTHASH_LIBRARY "bsaopthash") - -# Sources -set(SOURCE_FILES - hash.cpp -) - -add_library(${BSAOPTHASH_LIBRARY} STATIC ${SOURCE_FILES}) - -set(BSAOPTHASH_LIBRARIES ${BSAOPTHASH_LIBRARY}) - -link_directories(${CMAKE_CURRENT_BINARY_DIR}) -set(BSAOPTHASH_LIBRARIES ${BSAOPTHASH_LIBRARIES} PARENT_SCOPE) diff --git a/extern/BSAOpt/hash.cpp b/extern/BSAOpt/hash.cpp deleted file mode 100644 index 08c807da45..0000000000 --- a/extern/BSAOpt/hash.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* Version: MPL 1.1/LGPL 3.0 - * - * "The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - * License for the specific language governing rights and limitations - * under the License. - * - * The Original Code is BSAopt. - * - * The Initial Developer of the Original Code is - * Ethatron . Portions created by The Initial - * Developer are Copyright (C) 2011 The Initial Developer. - * All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the terms - * of the GNU Library General Public License Version 3 license (the - * "LGPL License"), in which case the provisions of LGPL License are - * applicable instead of those above. If you wish to allow use of your - * version of this file only under the terms of the LGPL License and not - * to allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and replace - * them with the notice and other provisions required by the LGPL License. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the MPL or the LGPL License." - */ - -#include -#include -#include - -#include "hash.hpp" - -std::uint32_t GenOBHashStr(const std::string& s) { - std::uint32_t hash = 0; - - for (std::size_t i = 0; i < s.length(); i++) { - hash *= 0x1003F; - hash += (unsigned char)s[i]; - } - - return hash; -} - -std::uint64_t GenOBHashPair(const std::string& fle, const std::string& ext) { - std::uint64_t hash = 0; - - if (fle.length() > 0) { - hash = (std::uint64_t)( - (((unsigned char)fle[fle.length() - 1]) * 0x1) + - ((fle.length() > 2 ? (unsigned char)fle[fle.length() - 2] : (unsigned char)0) * 0x100) + - (fle.length() * 0x10000) + - (((unsigned char)fle[0]) * 0x1000000) - ); - - if (fle.length() > 3) { - hash += (std::uint64_t)(GenOBHashStr(fle.substr(1, fle.length() - 3)) * 0x100000000); - } - } - - if (ext.length() > 0) { - hash += (std::uint64_t)(GenOBHashStr(ext) * 0x100000000LL); - - unsigned char i = 0; - if (ext == ".nif") i = 1; - if (ext == ".kf" ) i = 2; - if (ext == ".dds") i = 3; - if (ext == ".wav") i = 4; - - if (i != 0) { - unsigned char a = (unsigned char)(((i & 0xfc ) << 5) + (unsigned char)((hash & 0xff000000) >> 24)); - unsigned char b = (unsigned char)(((i & 0xfe ) << 6) + (unsigned char)( hash & 0x000000ff) ); - unsigned char c = (unsigned char)(( i << 7) + (unsigned char)((hash & 0x0000ff00) >> 8)); - - hash -= hash & 0xFF00FFFF; - hash += (std::uint32_t)((a << 24) + b + (c << 8)); - } - } - - return hash; -} - -std::uint64_t GenOBHash(const std::string& path, std::string& file) { - std::transform(file.begin(), file.end(), file.begin(), ::tolower); - std::replace(file.begin(), file.end(), '/', '\\'); - - std::string fle; - std::string ext; - - const char *_fle = file.data(); - const char *_ext = strrchr(_fle, '.'); - if (_ext) { - ext = file.substr((0 + _ext) - _fle); - fle = file.substr(0, ( _ext) - _fle); - } - else { - ext = ""; - fle = file; - } - - if (path.length() && fle.length()) - return GenOBHashPair(path + "\\" + fle, ext); - else - return GenOBHashPair(path + fle, ext); -} diff --git a/extern/BSAOpt/hash.hpp b/extern/BSAOpt/hash.hpp deleted file mode 100644 index cd936530a8..0000000000 --- a/extern/BSAOpt/hash.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* Version: MPL 1.1/LGPL 3.0 - * - * "The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - * License for the specific language governing rights and limitations - * under the License. - * - * The Original Code is BSAopt. - * - * The Initial Developer of the Original Code is - * Ethatron . Portions created by The Initial - * Developer are Copyright (C) 2011 The Initial Developer. - * All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the terms - * of the GNU Library General Public License Version 3 license (the - * "LGPL License"), in which case the provisions of LGPL License are - * applicable instead of those above. If you wish to allow use of your - * version of this file only under the terms of the LGPL License and not - * to allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and replace - * them with the notice and other provisions required by the LGPL License. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the MPL or the LGPL License." - */ -#ifndef BSAOPT_HASH_H -#define BSAOPT_HASH_H - -#include - -std::uint32_t GenOBHashStr(const std::string& s); - -std::uint64_t GenOBHashPair(const std::string& fle, const std::string& ext); - -std::uint64_t GenOBHash(const std::string& path, std::string& file); - -#endif // BSAOPT_HASH_H