diff --git a/.gitignore b/.gitignore index 259148f..fab7372 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,73 @@ -# Prerequisites -*.d +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- -# Compiled Object files -*.slo -*.lo +*~ +*.autosave +*.a +*.core +*.moc *.o *.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash -# Precompiled Headers -*.gch -*.pch +# qtcreator generated files +*.pro.user* -# Compiled Dynamic libraries -*.so -*.dylib -*.dll +# xemacs temporary files +*.flc -# Fortran module files -*.mod -*.smod +# Vim temporary files +.*.swp -# Compiled Static libraries -*.lai -*.la -*.a -*.lib +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* -# Executables +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll *.exe -*.out -*.app + diff --git a/AFKPexAnon.pro b/AFKPexAnon.pro new file mode 100644 index 0000000..c5006ce --- /dev/null +++ b/AFKPexAnon.pro @@ -0,0 +1,93 @@ +TEMPLATE = app +CONFIG += console c++14 +CONFIG -= app_bundle +CONFIG -= qt + +############################################ +## Library Source Dependencies: Boost, keeg +## Boost: http://www.boost.org +## keeg: https://github.com/namralkeeg/keeg +############################################ + +INCLUDEPATH += $$(BOOST_ROOT) $$(KEEG_ROOT)/src src + +SOURCES += \ + src/main.cpp \ + src/afk/fileformats/pex/pexheader.cpp \ + src/afk/fileformats/pex/pexbase.cpp \ + src/afk/fileformats/pex/pexskyrim.cpp \ + src/afk/fileformats/pex/pexskyrimse.cpp \ + src/afk/fileformats/pex/pexfallout4.cpp \ + src/afk/fileformats/pex/pexfactory.cpp \ + src/afk/afkpexanon.cpp + +HEADERS += \ + src/version.hpp \ + src/afk/fileformats/pex/gameid.hpp \ + src/afk/fileformats/pex/pexheader.hpp \ + src/afk/fileformats/pex/pexbase.hpp \ + src/afk/fileformats/pex/pexskyrim.hpp \ + src/afk/fileformats/pex/pexskyrimse.hpp \ + src/afk/fileformats/pex/pexfallout4.hpp \ + src/afk/fileformats/pex/pexfactory.hpp \ + src/afk/afkpexanon.hpp + +############################### +## Version Info +############################### + +win32:VERSION = 1.1.0 # major.minor.patch +else:VERSION = 1.1.0 # major.minor.patch + +# Debug/Release options +CONFIG(debug, debug|release) { + # Debug Options + win32-g++ { + LIBS += "-L$$(BOOST_LIBRARYDIR_MINGW)" + LIBS += -lboost_program_options-mgw53-mt-d-1_65_1 -lboost_filesystem-mgw53-mt-d-1_65_1 -lboost_system-mgw53-mt-d-1_65_1 + } +} else { + # Release Options + win32-g++ { + LIBS += "-L$$(BOOST_LIBRARYDIR_MINGW)" + LIBS += -lboost_program_options-mgw53-mt-1_65_1 -lboost_filesystem-mgw53-mt-1_65_1 -lboost_system-mgw53-mt-1_65_1 + } +} + +############################### +## COMPILER SCOPES +############################### + +*msvc* { + LIBS += "-L$$(BOOST_LIBRARYDIR)" + + # So VCProj Filters do not flatten headers/source + CONFIG -= flat + + # COMPILER FLAGS + # Optimization flags + QMAKE_CXXFLAGS_RELEASE -= /O2 + QMAKE_CXXFLAGS_RELEASE *= /O2 /Ot /Ox /GL + + # Multithreaded compiling for Visual Studio + QMAKE_CXXFLAGS += -MP + + # Linker flags + QMAKE_LFLAGS_RELEASE += /LTCG +} + +*-g++ { + # COMPILER FLAGS + + # Optimization flags + QMAKE_CXXFLAGS_DEBUG -= -O0 -g + QMAKE_CXXFLAGS_DEBUG *= -Og -g3 + QMAKE_CXXFLAGS_RELEASE -= -O2 + QMAKE_CXXFLAGS_RELEASE *= -O3 -mfpmath=sse + + # Extension flags + QMAKE_CXXFLAGS_RELEASE += -msse2 -msse + + # Linker flags + QMAKE_LFLAGS_RELEASE += -static +} diff --git a/README.md b/README.md index 4974b82..a3c650d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ # AFKPexAnon Application that anonymize's Bethesda's compiled Papyrus script files (.pex). + +Copyright (c) 2017 Larry Lopez + +See LICENSE file for licensing information. + +Dependencies +============ + +* Boost 1.65.1 http://www.boost.org/ +* keeg 1.0.0 https://github.com/namralkeeg/keeg +* A C++14 compiler. (for Windows you need at least Visual Studio 2015 or mingw 5.3) +* Qt Creator (qmake) + + +### Commandline Options +``` +Generic Options: + -h [ --help ] Help Screen + -c [ --config ] arg (=afkpexanon.cfg) Use configuration file. + --version Show application version information. + +Config Options: + -s [ --source ] arg (=.) Source Folder(s), defaults to current + folder. + -b [ --backup ] Enables the creation of backup Files. + -m [ --mask ] arg (=*) Character to mask computer and user + name. Defaults to * + -r [ --recursive ] Recursively process all subfolders. + --verbose Enables verbose output mode. +``` \ No newline at end of file diff --git a/src/afk/afkpexanon.cpp b/src/afk/afkpexanon.cpp new file mode 100644 index 0000000..c946bbb --- /dev/null +++ b/src/afk/afkpexanon.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "afkpexanon.hpp" +#include +#include +#include +#include +#include +#include + +namespace afk { + +using namespace std; +using namespace afk::fileformats::pex; +namespace bf = boost::filesystem; +namespace bpo = boost::program_options; + +AFKPexAnon::AFKPexAnon(const int &argc, char *argv[]) : m_argc(argc), m_argv(argv) +{ } + +int AFKPexAnon::run() +{ + if (!processProgramOptions()) + return EXIT_FAILURE; + + if (m_showVersion) + { + showVersion(); + if (!m_showHelp) + return EXIT_SUCCESS; + } + + if (m_showHelp) + { + showHelp(m_visibleOptions); + return EXIT_SUCCESS; + } + + try + { + std::vector entries; + for (const auto &dir: m_sourceFolders) + { + if (m_verboseMode) + std::cout << "Searching: " << dir << std::endl; + + /// Recursively add files from subfolders. + if (m_recursiveFolders) + { + for (auto &entry: traverseDirectoryRecursive(dir)) + if (isValidFile(entry)) + entries.push_back(entry.path()); + } + /// Only add files from the root of each specified folder. + else + { + for (auto &entry: traverseDirectory(dir)) + if (isValidFile(entry)) + entries.push_back(entry.path()); + } + } + + /// Sort the files in alphabetical order. + std::sort(std::begin(entries), std::end(entries)); + + if (entries.size() > 0) + std::cout << "Anonymizing " << entries.size() << " File(s):" << std::endl; + + for (bf::path &entry: entries) + { + /// Check if the file is a recognized type. + std::unique_ptr pexOrig; + { + ifstream entryFile(entry.string(), std::ios::binary); + pexOrig = fileformats::pex::PexFactory::createUniquePex(entryFile); + if (pexOrig) + pexOrig->read(entryFile); + } + + if (pexOrig) + { + if (m_backupFiles) + { + bf::path backupPath = entry; + backupPath.replace_extension(m_backupExtension); + + if (m_verboseMode) + std::cout << "Creating backup file: " << backupPath.string() << std::endl; + + if (!createBackupFile(entry)) + throw std::runtime_error("Unable to create backup file: " + backupPath.string()); + } + + std::cout << entry << std::endl; + if (m_verboseMode) + std::cout << *pexOrig << std::endl; + + /// Create a temporary working file in case there's an error. + if (backupAndChangeExt(entry, defaultTempExtension)) + { + bf::path tempPath = entry; + tempPath.replace_extension(defaultTempExtension); + std::unique_ptr pexDest; + { + ifstream destFile(tempPath.string(), std::ios::binary); + pexDest = PexFactory::createUniquePex(destFile); + if (pexDest) + pexDest->read(destFile); + } + + if (pexDest) + { + /// Fill the machine name with mask characters. + pexDest->setMachineName(std::string(pexDest->getMachineName().size(), m_mask)); + /// Fill the user name with mask characters. + pexDest->setUserName(std::string(pexDest->getUserName().size(), m_mask)); + + /// Strip the path from script names in fallout 4 pex's. + /// No idea why Bethesda in their infinte wisdom decided to add the path from the + /// temporary folder to the source file? + if (pexDest->getPexHeader().gameId == keeg::common::enumToIntegral(GameID::fallout4)) + { + pexDest->setSourceFileName(bf::path(pexDest->getSourceFileName()).filename().string()); + } + + /// Write out the changes to the temp file. + { + ofstream destFile(tempPath.string(), std::ios::binary | std::ios::trunc); + if (!pexDest->write(destFile)) + { + /// Failed to write out to the temp file properly. Attemp to clean up. + destFile.close(); + bf::remove(tempPath); + throw std::runtime_error("Unable to write to temporary file: " + tempPath.string()); + } + } + + /// Read the temp file back in and compare the data to the original. + { + ifstream destFile(tempPath.string(), std::ios::binary); + if (!pexDest->read(destFile)) + { + /// something bad happened. + /// Failed to read the temp file properly. Attemp to clean up. + destFile.close(); + bf::remove(tempPath); + throw std::runtime_error("Unable to read temporary file: " + tempPath.string()); + } + } + + /// Validate the data after the header and swap files if it's valid. + if ((pexOrig->getPexHeader() == pexDest->getPexHeader()) + && (pexOrig->getData() == pexDest->getData())) + { + bf::remove(entry); + bf::rename(tempPath, entry); + } + else + { + bf::remove(tempPath); + std::cout << "Unable to validate data skipping: " + entry.string() << std::endl; + } + } + /// Reading in the temp file failed. + else + { + throw std::runtime_error("Reading in the temp file: " + tempPath.string() + " failed."); + } + } + else + { + throw std::runtime_error("Unable to create temporary files"); + } + } + else + { + std::cout << "Unrecognized file type: " << entry << std::endl; + } + } + } + catch (std::exception const &ex) + { + std::cerr << ex.what() << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +bool AFKPexAnon::isValidFile(bf::directory_entry const &entry) +{ + try + { + auto it = std::find(std::begin(m_validExtensions), + std::end(m_validExtensions), + entry.path().extension().string()); + + return bf::is_regular_file(entry) + && !bf::is_other(entry) + && (it != std::end(m_validExtensions)); + } + catch (std::exception const &ex) + { + std::cerr << ex.what() << std::endl; + return false; + } +} + +bool AFKPexAnon::backupAndChangeExt(const boost::filesystem::path &filePath, const string &ext) +{ + try + { + bf::path backupFile = filePath; + backupFile.replace_extension(ext); + bf::copy_file(filePath, backupFile, bf::copy_option::fail_if_exists); + return true; + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return false; + } +} + +bool AFKPexAnon::createBackupFile(const bf::path &filePath) +{ + return backupAndChangeExt(filePath, m_backupExtension); +} + +bool AFKPexAnon::removeBackupFile(const bf::path &filePath) +{ + try + { + bf::path backupFile = filePath; + backupFile.replace_extension(m_backupExtension); + return bf::remove(backupFile); + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return false; + } +} + +void AFKPexAnon::showHelp(const bpo::options_description &desc) +{ + std::cout << desc << std::endl; +} + +void AFKPexAnon::showVersion() +{ + std::cout << afkPexAnonDesString << std::endl; +} + +bool AFKPexAnon::processProgramOptions() +{ + bpo::options_description genericOptions{"Generic Options"}; + bpo::options_description configOptions{"Config Options"}; + bpo::options_description hiddenOptions{"Hidden Options"}; + + genericOptions.add_options() + ( + "help,h", + bpo::value(&m_showHelp) + ->default_value(false) + ->implicit_value(true) + ->zero_tokens(), + "Help Screen" + ) + ( + "config,c", + bpo::value(&m_configFileName) + ->default_value(defaultConfigFileName), + "Use configuration file." + ) + ( + "version", + bpo::value(&m_showVersion) + ->default_value(false) + ->implicit_value(true) + ->zero_tokens(), + "Show application version information." + ); + + configOptions.add_options() + ( + "source,s", + bpo::value >(&m_sourceFolders) + ->default_value(std::vector(1, defaultCurrentFolder), defaultCurrentFolder) + ->multitoken() + ->composing(), + "Source Folder(s), defaults to current folder." + ) + ( + "backup,b", + bpo::value(&m_backupFiles) + ->default_value(false) + ->implicit_value(true) + ->zero_tokens(), + "Enables the creation of backup Files." + ) + ( + "mask,m", + bpo::value(&m_mask) + ->default_value('*'), + "Character to mask computer and user name. Defaults to *" + ) + ( + "recursive,r", + bpo::value(&m_recursiveFolders) + ->default_value(false) + ->implicit_value(true) + ->zero_tokens(), + "Recursively process all subfolders." + ) + ( + "verbose", + bpo::value(&m_verboseMode) + ->default_value(false) + ->implicit_value(true) + ->zero_tokens(), + "Enables verbose output mode." + ); + + m_posOptions.add("source", -1); + + hiddenOptions.add_options() + ( + "backup-extension", + bpo::value(&m_backupExtension) + ->default_value(defaultBackupExtension), + "Backup files extension." + ) + ( + "valid-extension", + bpo::value>(&m_validExtensions) + ->default_value(std::vector(1, defaultPexExtension), defaultPexExtension) + ->multitoken() + ->composing(), + "File Extensions to process." + ); + + m_cmdlineOptions.add(genericOptions).add(configOptions).add(hiddenOptions); + m_configFileOptions.add(configOptions).add(hiddenOptions); + m_visibleOptions.add(genericOptions).add(configOptions); + + bpo::command_line_parser parser{m_argc, m_argv}; + parser.options(m_cmdlineOptions).positional(m_posOptions).allow_unregistered().style( + bpo::command_line_style::default_style | + bpo::command_line_style::allow_slash_for_short + ); + + try + { + bpo::parsed_options parsedOptions = parser.run(); + bpo::store(parsedOptions, m_vm); + bpo::notify(m_vm); + + { + std::ifstream configFile{m_configFileName}; + if (configFile) + { + bpo::store(bpo::parse_config_file(configFile, m_configFileOptions), m_vm); + bpo::notify(m_vm); + } + } + + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return false; + } + + return true; +} + +} // afk namespace diff --git a/src/afk/afkpexanon.hpp b/src/afk/afkpexanon.hpp new file mode 100644 index 0000000..c7fe84e --- /dev/null +++ b/src/afk/afkpexanon.hpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef AFKPEXANON_HPP +#define AFKPEXANON_HPP + +#include +#include +#include +#include +#include +#include +#include "version.hpp" + +namespace afk { + +class AFKPexAnon +{ +public: + AFKPexAnon(const int &argc, char *argv[]); + inline virtual ~AFKPexAnon() { } + virtual int run(); + +protected: + template + boost::iterator_range traverseDirectoryRecursive(T const &dir) + { + boost::filesystem::recursive_directory_iterator l; + try + { + boost::filesystem::recursive_directory_iterator f(dir); + return boost::make_iterator_range(f, l); + } + catch(std::exception const &ex) + { + std::cerr << ex.what() << std::endl; + return boost::make_iterator_range(l, l); + } + } + + template + boost::iterator_range traverseDirectory(T const &dir) + { + boost::filesystem::directory_iterator l; + try + { + boost::filesystem::directory_iterator f(dir); + return boost::make_iterator_range(f, l); + } + catch (std::exception const &ex) + { + std::cerr << ex.what() << std::endl; + return boost::make_iterator_range(l, l); + } + } + + bool isValidFile(boost::filesystem::directory_entry const &entry); + + bool backupAndChangeExt(const boost::filesystem::path &filePath, const std::string &ext); + bool createBackupFile(const boost::filesystem::path &filePath); + bool removeBackupFile(const boost::filesystem::path &filePath); + + virtual bool processProgramOptions(); + virtual void showHelp(const boost::program_options::options_description &desc); + virtual void showVersion(); + +protected: + int m_argc; + char** m_argv; + + const std::string defaultBackupExtension{".bak"}; + const std::string defaultPexExtension{".pex"}; + const std::string defaultCurrentFolder{"."}; + const std::string defaultConfigFileName{"afkpexanon.cfg"}; + const std::string defaultTempExtension{".tmp"}; + const std::string afkPexAnonDesString{"AFKPexAnon PEX Anonymizer V"+version::VERSION_STRING}; + + /// Boost Program Options + boost::program_options::options_description m_cmdlineOptions{"Cmdline Options"}; + boost::program_options::options_description m_configFileOptions{"Config File Options"}; + boost::program_options::options_description m_visibleOptions{afkPexAnonDesString}; + boost::program_options::positional_options_description m_posOptions; + boost::program_options::variables_map m_vm; + + + /// Valid extensions of files to process. + std::vector m_validExtensions; + /// List of source root folders to search. + std::vector m_sourceFolders; + /// Extension to use for backup files. + std::string m_backupExtension; + /// Name of the config file. + std::string m_configFileName; + /// Backup files switch. + bool m_backupFiles; + /// Character to mask computer and user names. + char m_mask; + /// Recurse through sub-folders switch. + bool m_recursiveFolders; + /// Show help switch. + bool m_showHelp; + /// Show version switch. + bool m_showVersion; + /// Turn on verbose mode switch. + bool m_verboseMode; + +private: + +}; + +} // afk namespace + +#endif // AFKPEXANON_HPP diff --git a/src/afk/fileformats/pex/gameid.hpp b/src/afk/fileformats/pex/gameid.hpp new file mode 100644 index 0000000..b3b4fed --- /dev/null +++ b/src/afk/fileformats/pex/gameid.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef GAMEID_HPP +#define GAMEID_HPP + +#include + +namespace afk { namespace fileformats { namespace pex { + +enum class GameID : uint16_t +{ + none = 0x0000, + skyrim = 0x0001, + skyrimSE = skyrim, + fallout4 = 0x0002, +}; + +} // pex namespace +} // fileformats namespace +} // afk namespace + +#endif // GAMEID_HPP diff --git a/src/afk/fileformats/pex/pexbase.cpp b/src/afk/fileformats/pex/pexbase.cpp new file mode 100644 index 0000000..6caa5ef --- /dev/null +++ b/src/afk/fileformats/pex/pexbase.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +namespace ki = keeg::io; +namespace ke = keeg::endian; + +void PexBase::setPexHeader(const PexHeader &pexHeader) +{ + m_header = pexHeader; +} + +void PexBase::setSourceFileName(const std::string &sourceFileName) +{ + m_sourceFileName = sourceFileName; +} + +void PexBase::setUserName(const std::string &userName) +{ + m_userName = userName; +} + +void PexBase::setMachineName(const std::string &machineName) +{ + m_machineName = machineName; +} + +void PexBase::setData(const std::vector &data) +{ + m_data.resize(data.size()); + std::copy(std::begin(data), std::end(data), std::begin(m_data)); +} + +bool PexBase::isPex(std::istream &instream) +{ + try + { + if (instream) + { + instream.seekg(0, std::ios::beg); + if (readHeader(instream)) + { + if (isPex(m_header)) + return true; + } + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return false; + } + + return false; +} + +std::size_t PexBase::read(std::istream &instream) +{ + std::size_t status = 0; + try + { + if (instream) + { + if (isPex(instream)) + { + status = ki::readWString(instream, m_sourceFileName, m_endianOrder); + status = ki::readWString(instream, m_userName, m_endianOrder); + status = ki::readWString(instream, m_machineName, m_endianOrder); + + auto headerEndPosition = instream.tellg(); + instream.seekg(0, std::ios::end); + auto fileSize = instream.tellg(); + auto dataSize = fileSize - headerEndPosition; + + instream.seekg(headerEndPosition, std::ios::beg); + if (ki::readBytes(instream, m_data, dataSize)) + status = static_cast(fileSize); + } + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return status; + } + + return status; +} + +std::size_t PexBase::write(std::ostream &outstream) +{ + std::size_t status = 0; + try + { + if (outstream) + { + outstream.seekp(0, std::ios::beg); + status = writeHeader(outstream); + if (status) + { + status += ki::writeWString(outstream, m_sourceFileName, m_endianOrder); + status += ki::writeWString(outstream, m_userName, m_endianOrder); + status += ki::writeWString(outstream, m_machineName, m_endianOrder); + + status += ki::writeBytes(outstream, m_data, m_data.size()); + } + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return 0; + } + + return status; +} + +PexBase::PexBase(const keeg::endian::Order &endianOrder) : m_endianOrder(endianOrder) +{ } + +std::size_t PexBase::readHeader(std::istream &instream) +{ + std::size_t status = 0; + try + { + if (instream) + { + instream.seekg(0, std::ios::beg); + status = ki::readPODType(instream, m_header); + if (status) + { + switch (m_header.magic) { + case UINT32_C(0xFA57C0DE): + break; + /// File data is in reverse endian of the host machine. + case UINT32_C(0xDEC057FA): + m_header.magic = ke::swap(m_header.magic); + m_header.gameId = ke::swap(m_header.gameId); + m_header.compilationTime = ke::swap(m_header.compilationTime); + break; + default: + /// File probably isn't a pex file or an unknown version. + status = 0; + break; + } + } + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return 0; + } + + return status; +} + +std::size_t PexBase::writeHeader(std::ostream &outstream) +{ + std::size_t status = 0; + try + { + if (outstream) + { + PexHeader header; + + switch (m_endianOrder) { + case ke::Order::big: + header.magic = ke::native_to_big(m_header.magic); + header.majorVersion = ke::native_to_big(m_header.majorVersion); + header.minorVersion = ke::native_to_big(m_header.minorVersion); + header.gameId = ke::native_to_big(m_header.gameId); + header.compilationTime = ke::native_to_big(m_header.compilationTime); + break; + case ke::Order::little: + header.magic = ke::native_to_little(m_header.magic); + header.majorVersion = ke::native_to_little(m_header.majorVersion); + header.minorVersion = ke::native_to_little(m_header.minorVersion); + header.gameId = ke::native_to_little(m_header.gameId); + header.compilationTime = ke::native_to_little(m_header.compilationTime); + default: + header = m_header; + break; + } + + outstream.seekp(0, std::ios::beg); + status = ki::writePODType(outstream, header); + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return 0; + } + + return status; +} + +std::ostream & operator <<(std::ostream &o, const PexBase &pexBase) +{ + o << pexBase.getPexHeader(); + o << "Script name:\t" << pexBase.getSourceFileName() << std::endl; + o << "User Name:\t" << pexBase.getUserName() << std::endl; + o << "Machine Name:\t" << pexBase.getMachineName() << std::endl; + + return o; +} + +} // pex namespace +} // fileformats namespace +} // afk namespace diff --git a/src/afk/fileformats/pex/pexbase.hpp b/src/afk/fileformats/pex/pexbase.hpp new file mode 100644 index 0000000..2ce5451 --- /dev/null +++ b/src/afk/fileformats/pex/pexbase.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef PEXBASE_HPP +#define PEXBASE_HPP + +#include +#include +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +class PexBase +{ +public: + /// Getters + inline keeg::endian::Order getEndianOrder() const { return m_endianOrder; } + const PexHeader& getPexHeader() const { return m_header; } + inline std::string getSourceFileName() const { return m_sourceFileName; } + inline std::string getUserName() const { return m_userName; } + inline std::string getMachineName() const { return m_machineName; } + const std::vector& getData() const { return m_data; } + + /// Setters + void setPexHeader(const PexHeader &pexHeader); + void setSourceFileName(const std::string &sourceFileName); + void setUserName(const std::string &userName); + void setMachineName(const std::string &machineName); + void setData(const std::vector &data); + + /// Header version numbers identify the version of the pex file. + virtual bool isPex(const PexHeader &pexHeader) = 0; + virtual bool isPex(std::istream &instream); + + virtual std::size_t read(std::istream &instream); + virtual std::size_t write(std::ostream &outstream); + + inline virtual ~PexBase() { } + +protected: + keeg::endian::Order m_endianOrder; + PexHeader m_header; + std::string m_sourceFileName; + std::string m_userName; + std::string m_machineName; + std::vector m_data; + + /// Protected constructor for abstract virtual base class. + PexBase(const keeg::endian::Order &endianOrder); + + virtual std::size_t readHeader(std::istream &instream); + virtual std::size_t writeHeader(std::ostream &outstream); +}; + +std::ostream & operator <<(std::ostream &o, const PexBase &pexBase); + +} // pex namespace +} // fileformats namespace +} // afk namespace + +#endif // PEXBASE_HPP diff --git a/src/afk/fileformats/pex/pexfactory.cpp b/src/afk/fileformats/pex/pexfactory.cpp new file mode 100644 index 0000000..eb09b5c --- /dev/null +++ b/src/afk/fileformats/pex/pexfactory.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +std::unique_ptr PexFactory::createUniquePex(const std::string &game) +{ + std::unique_ptr pexBase; + + if (game == "skyrim") + pexBase = std::make_unique(); + else if (game == "skyrimSE") + pexBase = std::make_unique(); + else if (game == "fallout4") + pexBase = std::make_unique(); + else + pexBase = nullptr; + + return std::move(pexBase); +} + +std::unique_ptr PexFactory::createUniquePex(const GameID &gameID) +{ + std::unique_ptr pexBase = nullptr; + + if (gameID == GameID::skyrim) + pexBase = std::make_unique(); + else if (gameID == GameID::skyrimSE) + pexBase = std::make_unique(); + else if (gameID == GameID::fallout4) + pexBase = std::make_unique(); + + return std::move(pexBase); +} + +std::unique_ptr PexFactory::createUniquePex(const PexHeader &pexHeader) +{ + std::unique_ptr pexBase; + + if (PexSkyrim().isPex(pexHeader)) + pexBase = std::make_unique(); + else if (PexSkyrimSE().isPex(pexHeader)) + pexBase = std::make_unique(); + else if (PexFallout4().isPex(pexHeader)) + pexBase = std::make_unique(); + else + pexBase = nullptr; + + return pexBase; +} + +std::unique_ptr PexFactory::createUniquePex(std::istream &instream) +{ + std::unique_ptr pexBase = nullptr; + + PexHeader pexHeader; + if (pexHeader.read(instream)) + { + if (PexSkyrim().isPex(pexHeader)) + pexBase = std::make_unique(); + else if (PexSkyrimSE().isPex(pexHeader)) + pexBase = std::make_unique(); + else if (PexFallout4().isPex(pexHeader)) + pexBase = std::make_unique(); + } + + return std::move(pexBase); +} + +} // pex namespace +} // fileformats namespace +} // afk namespace diff --git a/src/afk/fileformats/pex/pexfactory.hpp b/src/afk/fileformats/pex/pexfactory.hpp new file mode 100644 index 0000000..c22e297 --- /dev/null +++ b/src/afk/fileformats/pex/pexfactory.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef PEXFACTORY_HPP +#define PEXFACTORY_HPP + +#include +#include +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +class PexFactory +{ +public: + static std::unique_ptr createUniquePex(const std::string &game); + static std::unique_ptr createUniquePex(const GameID &gameID); + static std::unique_ptr createUniquePex(const PexHeader &pexHeader); + static std::unique_ptr createUniquePex(std::istream &instream); + +protected: + PexFactory() { } +}; + +} // pex namespace +} // fileformats namespace +} // afk namespace + +#endif // PEXFACTORY_HPP diff --git a/src/afk/fileformats/pex/pexfallout4.cpp b/src/afk/fileformats/pex/pexfallout4.cpp new file mode 100644 index 0000000..b536000 --- /dev/null +++ b/src/afk/fileformats/pex/pexfallout4.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +namespace kc = keeg::common; + +/// Fallout 4 pex files use Little-Endian ordering for numbers. +PexFallout4::PexFallout4() : PexBase(keeg::endian::Order::little) +{ + m_header.magic = UINT32_C(0xFA57C0DE); + m_header.majorVersion = 3; + m_header.minorVersion = 9; + m_header.gameId = kc::enumToIntegral(GameID::fallout4); + m_header.compilationTime = static_cast(time(NULL)); +} + +bool PexFallout4::isPex(const PexHeader &pexHeader) +{ + try + { + if ((pexHeader.magic == UINT32_C(0xFA57C0DE)) && + (pexHeader.majorVersion == 3) && + (pexHeader.minorVersion == 9) && + (pexHeader.gameId == kc::enumToIntegral(GameID::fallout4))) + { + return true; + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return false; + } + + return false; +} + +} // pex namespace +} // fileformats namespace +} // afk namespace diff --git a/src/afk/fileformats/pex/pexfallout4.hpp b/src/afk/fileformats/pex/pexfallout4.hpp new file mode 100644 index 0000000..f0e5a4f --- /dev/null +++ b/src/afk/fileformats/pex/pexfallout4.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef PEXFALLOUT4_HPP +#define PEXFALLOUT4_HPP + +#include + +namespace afk { namespace fileformats { namespace pex { + +class PexFallout4 : public PexBase +{ +public: + /// Fallout 4 pex files use Little-Endian ordering for numbers. + PexFallout4(); + virtual ~PexFallout4() { } + + virtual bool isPex(const PexHeader &pexHeader) override; +}; + +} // pex namespace +} // fileformats namespace +} // afk namespace + +#endif // PEXFALLOUT4_HPP diff --git a/src/afk/fileformats/pex/pexheader.cpp b/src/afk/fileformats/pex/pexheader.cpp new file mode 100644 index 0000000..b42e635 --- /dev/null +++ b/src/afk/fileformats/pex/pexheader.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +std::size_t PexHeader::read(std::istream &instream) +{ + try + { + if (instream) + { + instream.seekg(0, std::ios::beg); + if (keeg::io::readPODType(instream, *this)) + { + std::size_t status = 0; + switch (magic) { + case UINT32_C(0xFA57C0DE): + status = 1; + break; + /// File data is in reverse endian of the host machine. + case UINT32_C(0xDEC057FA): + magic = keeg::endian::swap(magic); + gameId = keeg::endian::swap(gameId); + compilationTime = keeg::endian::swap(compilationTime); + status = 1; + break; + default: + break; + } + + return status; + } + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return 0; + } + + return 0; +} + +std::ostream & operator <<(std::ostream &o, const PexHeader &pexHeader) +{ + char buildTimeStr[27]; + time_t compTime = static_cast(pexHeader.compilationTime); + ctime_s(buildTimeStr, sizeof(buildTimeStr), &compTime); + buildTimeStr[24] = 0; // remove newline (format is fixed-length, no really) + + o << "Magic:\t" << std::hex << pexHeader.magic << std::endl; + o << "Major:\t" << std::hex << std::setfill('0') << std::setw(2) + << static_cast(pexHeader.majorVersion) << std::endl; + o << "Minor:\t" << std::hex << std::setfill('0') << std::setw(2) + << static_cast(pexHeader.minorVersion) << std::endl; + o << "Game ID\t: " << std::hex << std::setfill('0') << std::setw(4) << pexHeader.gameId << std::endl; + o << "Compile:\t" << std::string(buildTimeStr) << std::endl; + + return o; +} + +bool operator ==(const PexHeader &lhs, const PexHeader &rhs) +{ + return (lhs.magic == rhs.magic) && + (lhs.majorVersion == rhs.majorVersion) && + (lhs.minorVersion == rhs.minorVersion) && + (lhs.gameId == rhs.gameId) && + (lhs.compilationTime == rhs.compilationTime); +} + +} // pex namespace +} // fileformats namespace +} // afk namespace diff --git a/src/afk/fileformats/pex/pexheader.hpp b/src/afk/fileformats/pex/pexheader.hpp new file mode 100644 index 0000000..8f1a71d --- /dev/null +++ b/src/afk/fileformats/pex/pexheader.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef PEXHEADER_HPP +#define PEXHEADER_HPP + +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +struct PexHeader +{ + uint32_t magic; // 00 FA57C0DE + uint8_t majorVersion; // 04 03=Skyrim, SSE, Fallout 4 + uint8_t minorVersion; // 05 02=Skyrim, 01=SkyrimSE, 09=Fallout 4 + uint16_t gameId; // 06 0001=Skyrim and SSE, 0002=Fallout 4 + uint64_t compilationTime; // 08 time_t usually uint64_t + + std::size_t read(std::istream &instream); +}; + +std::ostream & operator <<(std::ostream &o, const PexHeader &pexHeader); +bool operator ==(const PexHeader &lhs, const PexHeader &rhs); + +} // pex namespace +} // fileformats namespace +} // afk namespace + +#endif // PEXHEADER_HPP diff --git a/src/afk/fileformats/pex/pexskyrim.cpp b/src/afk/fileformats/pex/pexskyrim.cpp new file mode 100644 index 0000000..b758d58 --- /dev/null +++ b/src/afk/fileformats/pex/pexskyrim.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +namespace kc = keeg::common; + +/// Skyrim pex files use Big-Endian ordering for numbers. +PexSkyrim::PexSkyrim() : PexBase(keeg::endian::Order::big) +{ + m_header.magic = UINT32_C(0xFA57C0DE); + m_header.majorVersion = 3; + m_header.minorVersion = 2; + m_header.gameId = kc::enumToIntegral(GameID::skyrim); + m_header.compilationTime = static_cast(time(NULL)); +} + +bool PexSkyrim::isPex(const PexHeader &pexHeader) +{ + try + { + if ((pexHeader.magic == UINT32_C(0xFA57C0DE)) && + (pexHeader.majorVersion == 3) && + (pexHeader.minorVersion == 2) && + (pexHeader.gameId == kc::enumToIntegral(GameID::skyrim))) + { + return true; + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return false; + } + + return false; +} + +} // pex namespace +} // fileformats namespace +} // afk namespace diff --git a/src/afk/fileformats/pex/pexskyrim.hpp b/src/afk/fileformats/pex/pexskyrim.hpp new file mode 100644 index 0000000..7a800d1 --- /dev/null +++ b/src/afk/fileformats/pex/pexskyrim.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef PEXSKYRIM_HPP +#define PEXSKYRIM_HPP + +#include + +namespace afk { namespace fileformats { namespace pex { + +class PexSkyrim : public PexBase +{ +public: + /// Skyrim pex files use Big-Endian ordering for numbers. + PexSkyrim(); + virtual ~PexSkyrim() { } + + virtual bool isPex(const PexHeader &pexHeader) override; +}; + +} // pex namespace +} // fileformats namespace +} // afk namespace + +#endif // PEXSKYRIM_HPP diff --git a/src/afk/fileformats/pex/pexskyrimse.cpp b/src/afk/fileformats/pex/pexskyrimse.cpp new file mode 100644 index 0000000..512054a --- /dev/null +++ b/src/afk/fileformats/pex/pexskyrimse.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include + +namespace afk { namespace fileformats { namespace pex { + +namespace kc = keeg::common; + +/// SkyrimSE pex files use Big-Endian ordering for numbers. +PexSkyrimSE::PexSkyrimSE() : PexBase(keeg::endian::Order::big) +{ + m_header.magic = UINT32_C(0xFA57C0DE); + m_header.majorVersion = 3; + m_header.minorVersion = 1; + m_header.gameId = kc::enumToIntegral(GameID::skyrimSE); + m_header.compilationTime = static_cast(time(NULL)); +} + +bool PexSkyrimSE::isPex(const PexHeader &pexHeader) +{ + try + { + if ((pexHeader.magic == UINT32_C(0xFA57C0DE)) && + (pexHeader.majorVersion == 3) && + (pexHeader.minorVersion == 1) && + (pexHeader.gameId == kc::enumToIntegral(GameID::skyrimSE))) + { + return true; + } + } + catch (const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return false; + } + + return false; +} + +} // pex namespace +} // fileformats namespace +} // afk namespace diff --git a/src/afk/fileformats/pex/pexskyrimse.hpp b/src/afk/fileformats/pex/pexskyrimse.hpp new file mode 100644 index 0000000..3b75dae --- /dev/null +++ b/src/afk/fileformats/pex/pexskyrimse.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef PEXSKYRIMSE_HPP +#define PEXSKYRIMSE_HPP + +#include + +namespace afk { namespace fileformats { namespace pex { + +class PexSkyrimSE : public PexBase +{ +public: + /// SkyrimSE pex files use Big-Endian ordering for numbers. + PexSkyrimSE(); + virtual ~PexSkyrimSE() { } + + virtual bool isPex(const PexHeader &pexHeader) override; +}; + +} // pex namespace +} // fileformats namespace +} // afk namespace + +#endif // PEXSKYRIMSE_HPP diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e1f5337 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 Larry Lopez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include + +using namespace std; + +int main(int argc, char* argv[]) +{ + int status = EXIT_FAILURE; + try + { + unique_ptr afkPexAnon = make_unique(argc, argv); + status = afkPexAnon->run(); + } + catch (exception const &ex) + { + cerr << ex.what() << endl; + } + + return status; +} diff --git a/src/version.hpp b/src/version.hpp new file mode 100644 index 0000000..ff1beed --- /dev/null +++ b/src/version.hpp @@ -0,0 +1,20 @@ +#ifndef VERSION_H +#define VERSION_H + +#include +#include + +namespace version +{ + +static const uint32_t MAJOR = UINT32_C(1); +static const uint32_t MINOR = UINT32_C(1); +static const uint32_t PATCH = UINT32_C(0); + +static const std::string VERSION_STRING{std::to_string(MAJOR) + "." + + std::to_string(MINOR) + "." + + std::to_string(PATCH)}; + +} // version namespace + +#endif // VERSION_H