他人の空似自作物置場

thxxbgmTh155Patch.zip/src/th145pak.h


namespace TouhouSE {

namespace TH145Pak {

namespace {

const unsigned char PUBLIC_EXPONENT[3] = {
  0x01, 0x00, 0x01,
};

const unsigned int MAX_DECRYPT_SIZE = 32;
const unsigned int BUF_SIZE = 64;

struct closeAlg {
  static void close(const BCRYPT_ALG_HANDLE handle) {
    ::BCryptCloseAlgorithmProvider(handle, 0);
  }
};

class DecryptEngine : public boost::noncopyable {
private:
  const std::unique_ptr<std::remove_pointer<BCRYPT_ALG_HANDLE>::type, decltype(&closeAlg::close)> alg;
  const std::unique_ptr<std::remove_pointer<BCRYPT_KEY_HANDLE>::type, decltype(&::BCryptDestroyKey)> key;

public:
  DecryptEngine(std::unique_ptr<std::remove_pointer<BCRYPT_ALG_HANDLE>::type, decltype(&closeAlg::close)> alg,
    std::unique_ptr<std::remove_pointer<BCRYPT_KEY_HANDLE>::type, decltype(&::BCryptDestroyKey)> key)
    : alg(std::move(alg)), key(std::move(key))
  {
  }

  static std::unique_ptr<DecryptEngine> create(const std::array<unsigned char, 64> &keySrc) {
    BCRYPT_ALG_HANDLE algRaw = NULL;
    if (!NT_SUCCESS(::BCryptOpenAlgorithmProvider(&algRaw, BCRYPT_RSA_ALGORITHM, NULL, 0))) {
      return{};
    }
    std::unique_ptr<std::remove_pointer<BCRYPT_ALG_HANDLE>::type, decltype(&closeAlg::close)> alg(algRaw, &closeAlg::close);
    auto key = importRsaPublicKey(alg.get(), keySrc);
    if (!key) {
      return{};
    }
    return std::make_unique<DecryptEngine>(std::move(alg), std::move(key));
  }

  static std::unique_ptr<std::remove_pointer<BCRYPT_KEY_HANDLE>::type, decltype(&::BCryptDestroyKey)> importRsaPublicKey(BCRYPT_ALG_HANDLE alg, const std::array<unsigned char, 64> &key) {
    static std::vector<unsigned char> keyData(sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(PUBLIC_EXPONENT) + key.size());
    BCRYPT_RSAKEY_BLOB &keyInfo = *reinterpret_cast<BCRYPT_RSAKEY_BLOB *>(&keyData.front());
    keyInfo.Magic = BCRYPT_RSAPUBLIC_MAGIC;
    keyInfo.BitLength = key.size() * 8;
    keyInfo.cbPublicExp = sizeof(PUBLIC_EXPONENT);
    keyInfo.cbModulus = key.size();
    keyInfo.cbPrime1 = 0;
    keyInfo.cbPrime2 = 0;
    unsigned char *ptr = &keyData[sizeof(BCRYPT_RSAKEY_BLOB)];
    for (unsigned int i = 0; i < sizeof(PUBLIC_EXPONENT); i++) {
      ptr[i] = PUBLIC_EXPONENT[i];
    }
    ptr = &keyData[sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(PUBLIC_EXPONENT)];
    for (unsigned int i = 0; i < key.size(); i++) {
      ptr[i] = key[i];
    }
    BCRYPT_KEY_HANDLE keyRaw = NULL;
    if (!NT_SUCCESS(::BCryptImportKeyPair(alg, NULL, BCRYPT_RSAPUBLIC_BLOB, &keyRaw, &keyData.front(), keyData.size(), 0))) {
      return{ NULL, ::BCryptDestroyKey };
    }
    return{ keyRaw, ::BCryptDestroyKey };
  }

  bool decrypt(const unsigned char * const in, const unsigned int inSize, unsigned char * const out, const unsigned int outSize) {
    const unsigned int bufSize = BUF_SIZE;
    if (in == nullptr || out == nullptr || inSize != bufSize || outSize > MAX_DECRYPT_SIZE) {
      return false;
    }
    std::vector<unsigned char> buf(bufSize);
    ULONG size = 0;
    if (!NT_SUCCESS(::BCryptEncrypt(key.get(), const_cast<unsigned char *>(in), bufSize, NULL, NULL, 0, &buf.front(), buf.size(), &size, BCRYPT_PAD_NONE))) {
      return false;
    }
    unsigned char * const signatureEndIt = &buf[bufSize - outSize];
    if (buf[0] != 0x00 || buf[1] != 0x01 || signatureEndIt[-1] != 0x00 || std::find_if(&buf[2], signatureEndIt - 1, [](const unsigned char a) {return a != 0xFF; }) != signatureEndIt - 1) {
      return false;
    }
    const std::vector<unsigned char>::const_iterator start = buf.end() - outSize;
    const std::vector<unsigned char>::const_iterator end = buf.end();
    std::copy(start, end, stdext::checked_array_iterator<unsigned char *>(out, outSize));
    return true;
  }
};

class FolderInfo {
public:
  unsigned int hash;
  unsigned int fileCount;

  FolderInfo(const unsigned int hash, const unsigned int fileCount) : hash(hash), fileCount(fileCount) {
  }

  static bool Read(std::istream &in, DecryptEngine &engine) {
    auto result = std::make_shared<std::vector<std::shared_ptr<FolderInfo> > >();
    std::vector<std::shared_ptr<FolderInfo> > &folderList = *result;
    std::array<unsigned char, BUF_SIZE> buf;
    in.read(reinterpret_cast<char *>(&buf[0]), buf.size());
    if (!in.good()) {
      return false;
    }
    unsigned int folderCount;
    if (!engine.decrypt(&buf[0], buf.size(), reinterpret_cast<unsigned char *>(&folderCount), sizeof(folderCount))) {
      return false;
    }
    if (folderCount == 0) {
      return true;
    }

    for (const unsigned int i : boost::irange(0U, folderCount)) {
      in.read(reinterpret_cast<char *>(&buf[0]), buf.size());
      if (!in.good()) {
        return false;
      }
      unsigned int info[2];
      if (!engine.decrypt(&buf[0], buf.size(), reinterpret_cast<unsigned char *>(&info[0]), sizeof(info))) {
        return false;
      }
      folderList.push_back(std::make_shared<FolderInfo>(info[0], info[1]));
    }
    const unsigned int fileCount = std::accumulate(folderList.begin(), folderList.end(), 0U, [](const unsigned int result, const std::shared_ptr<const FolderInfo> folder) {
      return result + folder->fileCount;
    });
    if (fileCount == 0) {
      return true;
    }

    struct {
      unsigned int compressSize;
      unsigned int size;
      unsigned int blockCount;
    } fileNameListInfo;
    in.read(reinterpret_cast<char *>(&buf[0]), buf.size());
    if (!in.good()) {
      return false;
    }
    if (!engine.decrypt(&buf[0], buf.size(), reinterpret_cast<unsigned char *>(&fileNameListInfo), sizeof(fileNameListInfo))) {
      return false;
    }
    in.seekg(buf.size() * fileNameListInfo.blockCount, std::ios::cur);
    return true;
  }
};

class FileInfo : boost::noncopyable {
public:
  unsigned int address;
  unsigned int size;
  unsigned int hash;
  unsigned int unknown;
  std::array<unsigned char, 16> key;

  FileInfo(const unsigned int address, const unsigned int size, const unsigned int hash, const unsigned int unknown, const std::array<unsigned char, 16> key)
    : address(address), size(size), hash(hash), unknown(unknown), key(key)
  {
  }
};

class FileInfoAndName : public boost::noncopyable {
public:
  std::shared_ptr<const FileInfo> info;
  std::wstring name;

  FileInfoAndName(const std::shared_ptr<const FileInfo> info, const std::wstring name) : info(info), name(name) {
  }
};

} // anonymouse

class Th145PakExtracter {
public:
  std::unique_ptr<std::istream> in;

private:
  const unsigned long long int fileSize;
  const std::map<unsigned int, std::shared_ptr<const FileInfo> > fileList;

  Th145PakExtracter(std::unique_ptr<std::istream> in, const unsigned long long int fileSize, std::map<unsigned int, std::shared_ptr<const FileInfo> > fileList) :
    in(std::move(in)), fileSize(fileSize), fileList(std::move(fileList))
  {
  }

  static unsigned int calcFNV1aHash(const std::wstring &in, const unsigned int init = 0x811C9DC5) {
    const std::string temp = TH135::Th135ArchiveExtractorBase<void>::wStrToStr(TH135::Th135ArchiveExtractorBase<void>::normalize(in));
    return std::accumulate(temp.begin(), temp.end(), init, [](const unsigned int cur, const unsigned char c) {
      return (cur ^ c) * 0x1000193;
    });
  }

public:
  static boost::shared_ptr<Th145PakExtracter> Open(std::unique_ptr<std::istream> inPtr, const unsigned long long int fileSize, const std::array<unsigned char, 64> &rsaKey) {
    std::istream &in = *inPtr;
    unsigned char signature[4];
    in.read(reinterpret_cast<char *>(signature), sizeof(signature));
    if (!in.good() || !std::equal(&signature[0], &signature[_countof(signature)], "TFPK")) {
      return{};
    }
    unsigned char version;
    in.read(reinterpret_cast<char *>(&version), sizeof(version));
    if (!in.good() || version != 1) {
      return{};
    }
    const std::unique_ptr<DecryptEngine> engine = DecryptEngine::create(rsaKey);
    if (!engine) {
      return{};
    }
    if (!FolderInfo::Read(in, *engine)) {
      return{};
    }
    std::array<unsigned char, BUF_SIZE> buf;
    in.read(reinterpret_cast<char *>(&buf[0]), buf.size());
    if (!in.good()) {
      return{};
    }
    unsigned int fileCount;
    if (!engine->decrypt(&buf[0], buf.size(), reinterpret_cast<unsigned char *>(&fileCount), sizeof(fileCount))) {
      return{};
    }
    const unsigned int baseAddress = static_cast<unsigned int>(in.tellg()) + fileCount * (3 * BUF_SIZE);
    std::vector<std::shared_ptr<const FileInfo> > fileInfoList(fileCount);
    for (std::shared_ptr<const FileInfo> &ptr : fileInfoList) {
      in.read(reinterpret_cast<char *>(&buf[0]), buf.size());
      if (!in.good()) {
        return{};
      }
      unsigned int sizeAndAddress[2];
      if (!engine->decrypt(&buf[0], buf.size(), reinterpret_cast<unsigned char *>(&sizeAndAddress), sizeof(sizeAndAddress))) {
        return{};
      }
      in.read(reinterpret_cast<char *>(&buf[0]), buf.size());
      if (!in.good()) {
        return{};
      }
      unsigned int unknownList[2];
      if (!engine->decrypt(&buf[0], buf.size(), reinterpret_cast<unsigned char *>(&unknownList), sizeof(unknownList))) {
        return{};
      }
      in.read(reinterpret_cast<char *>(&buf[0]), buf.size());
      if (!in.good()) {
        return{};
      }
      std::array<unsigned char, 16> key;
      if (!engine->decrypt(&buf[0], buf.size(), &key[0], key.size())) {
        return{};
      }
      const unsigned int size = sizeAndAddress[0] ^ *reinterpret_cast<const unsigned int *>(&key[0]);
      const unsigned int address = baseAddress + (sizeAndAddress[1] ^ *reinterpret_cast<const unsigned int *>(&key[4]));
      const unsigned int hash = (~(unknownList[0] ^ *reinterpret_cast<const unsigned int *>(&key[8]))) + 1;
      const unsigned int unknown3 = unknownList[1] ^ *reinterpret_cast<const unsigned int *>(&key[12]);
      *reinterpret_cast<unsigned int *>(&key[0]) = (~*reinterpret_cast<const unsigned int *>(&key[0])) + 1;
      *reinterpret_cast<unsigned int *>(&key[4]) = (~*reinterpret_cast<const unsigned int *>(&key[4])) + 1;
      *reinterpret_cast<unsigned int *>(&key[8]) = (~*reinterpret_cast<const unsigned int *>(&key[8])) + 1;
      *reinterpret_cast<unsigned int *>(&key[12]) = (~*reinterpret_cast<const unsigned int *>(&key[12])) + 1;
      ptr.reset(new FileInfo(address, size, hash, unknown3, key));
    }
    std::map<unsigned int, std::shared_ptr<const FileInfo> > fileMap;
    for (const std::shared_ptr<const FileInfo> item : fileInfoList) {
      fileMap.insert({item->hash, item});
    }
    return boost::shared_ptr<Th145PakExtracter>(new Th145PakExtracter(std::move(inPtr), fileSize, std::move(fileMap)));
  }

  const FileInfo *Find(const boost::filesystem::path &path) const {
    const auto it = fileList.find(calcFNV1aHash(path.wstring()));
    if (it == fileList.end()) {
      return nullptr;
    }
    return &*it->second;
  }
};

} // TH145Pak

} // TouhouSE