thxxbgmTh155Patch.zip/src/main.cpp
#include "stdafx.h"
// 東方憑依華を有効にする
#define ENABLE_TH155
// 東方深秘録を有効にする
#define ENABLE_TH145
// 東方心綺楼を有効にする
#define ENABLE_TH135
namespace {
constexpr unsigned int SIGNATURE_MASK = 0xFFFF0000;
constexpr unsigned int ARG_MASK = 0x0000FFFF;
constexpr unsigned int SIGNATURE_FIND_FILE_W = 0x00010000;
constexpr unsigned int SIGNATURE_NATIVE_FILE = 0x00020000;
constexpr unsigned int SIGNATURE_VIRTUAL_FILE = 0x00030000;
struct CloseHandleProc {
void operator()(const HANDLE handle) const {
::CloseHandle(handle);
}
};
class VirtualFileBase {
public:
enum SeekType {
BEGIN, CURRENT, END
};
virtual ~VirtualFileBase() {};
virtual unsigned int tell() const = 0;
virtual bool seek(unsigned int dis, const SeekType seekType) = 0;
virtual unsigned int read(unsigned char * const buf, const unsigned int size) = 0;
};
typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, CloseHandleProc> NativeFileHandle;
typedef std::unique_ptr<VirtualFileBase> VirtualFileHandle;
std::vector<std::pair<unsigned int, std::vector<WIN32_FIND_DATAW> > > findFileAryW;
std::vector<NativeFileHandle> nativeFileAry;
std::vector<VirtualFileHandle> virtualFileAry;
unsigned int getArg(const unsigned int handle) {
return handle & ARG_MASK;
}
unsigned int getSignature(const unsigned int handle) {
return handle & SIGNATURE_MASK;
}
unsigned int isNativeFileHandle(const unsigned int handle) {
return getSignature(handle) == SIGNATURE_NATIVE_FILE;
}
unsigned int isVirtualFileHandle(const unsigned int handle) {
return getSignature(handle) == SIGNATURE_VIRTUAL_FILE;
}
bool isKnownHandle(const unsigned int handle) {
const unsigned int arg = getArg(handle);
switch (getSignature(handle)) {
case SIGNATURE_FIND_FILE_W:
return arg < findFileAryW.size();
case SIGNATURE_NATIVE_FILE:
return arg < nativeFileAry.size();
case SIGNATURE_VIRTUAL_FILE:
return arg < virtualFileAry.size();
default:
return false;
}
}
unsigned int newHandle(const unsigned int signature) {
switch (signature) {
case SIGNATURE_FIND_FILE_W:
findFileAryW.resize(findFileAryW.size() + 1);
return SIGNATURE_FIND_FILE_W + (findFileAryW.size() - 1);
case SIGNATURE_NATIVE_FILE:
nativeFileAry.resize(nativeFileAry.size() + 1);
return SIGNATURE_NATIVE_FILE + (nativeFileAry.size() - 1);
case SIGNATURE_VIRTUAL_FILE:
virtualFileAry.resize(virtualFileAry.size() + 1);
return SIGNATURE_VIRTUAL_FILE + (virtualFileAry.size() - 1);
default:
assert(false);
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
}
unsigned int newFindFileWHandle() {
return newHandle(SIGNATURE_FIND_FILE_W);
}
unsigned int newNativeFileHandle() {
return newHandle(SIGNATURE_NATIVE_FILE);
}
unsigned int newVirtualFileHandle() {
return newHandle(SIGNATURE_VIRTUAL_FILE);
}
template<typename CONTAINER, typename IS_EMPTY_PROC, typename CLEAR_PROC>
void closeAnyHandle(const unsigned int arg, CONTAINER &container, const IS_EMPTY_PROC &isEmptyProc, const CLEAR_PROC &clearProc) {
const unsigned int curSize = container.size();
if (arg < curSize - 1) {
clearProc(container[arg]);
return;
}
const std::reverse_iterator<CONTAINER::iterator> begin(container.end());
const std::reverse_iterator<CONTAINER::iterator> end(container.begin());
const std::reverse_iterator<CONTAINER::iterator> it = std::find_if(
begin + 1,
end,
[isEmptyProc](const auto &item) {
return !isEmptyProc(item);
}
);
container.resize(container.size() - std::distance(begin, it));
}
void closeFindFileW(const unsigned int arg) {
closeAnyHandle(
arg,
findFileAryW,
[](const std::pair<unsigned int, std::vector<WIN32_FIND_DATAW> > &item) { return item.second.empty(); },
[](std::pair<unsigned int, std::vector<WIN32_FIND_DATAW> > &item) { item.second.clear(); });
}
void closeNativeFileHandle(const unsigned int arg) {
closeAnyHandle(arg, nativeFileAry, [](const NativeFileHandle &item) { return !item; }, [](NativeFileHandle &item) { item.reset(); });
}
void closeVirtualFileHandle(const unsigned int arg) {
closeAnyHandle(arg, virtualFileAry, [](const VirtualFileHandle &item) { return !item; }, [](VirtualFileHandle &item) { item.reset(); });
}
void closeHandle(const unsigned int handle) {
const unsigned int arg = getArg(handle);
switch (getSignature(handle)) {
case SIGNATURE_FIND_FILE_W:
closeFindFileW(arg);
return;
case SIGNATURE_NATIVE_FILE:
closeNativeFileHandle(arg);
return;
case SIGNATURE_VIRTUAL_FILE:
closeVirtualFileHandle(arg);
return;
default:
assert(false);
return;
}
}
std::pair<unsigned int, std::vector<WIN32_FIND_DATAW> > &getFindFileAryW(const unsigned int handle) {
assert(getSignature(handle) == SIGNATURE_FIND_FILE_W);
return findFileAryW[getArg(handle)];
}
void hookFindFirstFileW(const wchar_t * const lpFileName, std::vector<WIN32_FIND_DATAW> &data);
HANDLE __stdcall d_FindFirstFileW(const wchar_t * const lpFileName, WIN32_FIND_DATAW * const lpFindFileData) {
const unsigned int handle = newFindFileWHandle();
std::pair<unsigned int, std::vector<WIN32_FIND_DATAW> > &data = getFindFileAryW(handle);
hookFindFirstFileW(lpFileName, data.second);
WIN32_FIND_DATAW item = { 0 };
HANDLE win32handle = INVALID_HANDLE_VALUE;
for (win32handle = ::FindFirstFileW(lpFileName, &item); win32handle != INVALID_HANDLE_VALUE; ) {
data.second.push_back(item);
if (::FindNextFileW(win32handle, &item) == FALSE) {
break;
}
}
::FindClose(win32handle);
if (data.second.empty()) {
closeHandle(handle);
return INVALID_HANDLE_VALUE;
}
data.first = 1;
*lpFindFileData = data.second[0];
return reinterpret_cast<HANDLE>(handle);
}
BOOL __stdcall d_FindNextFileW(const HANDLE hFindFile, WIN32_FIND_DATAW * const lpFindFileData) {
const unsigned int handle = reinterpret_cast<unsigned int>(hFindFile);
std::pair<unsigned int, std::vector<WIN32_FIND_DATAW> > &data = getFindFileAryW(handle);
if (data.first >= data.second.size()) {
::SetLastError(ERROR_NO_MORE_FILES);
return FALSE;
}
*lpFindFileData = data.second[data.first];
data.first++;
return TRUE;
}
BOOL __stdcall d_FindClose(HANDLE hFindFile) {
closeHandle(reinterpret_cast<unsigned int>(hFindFile));
return TRUE;
}
class VirtualFileOnMemory : public VirtualFileBase {
private:
const std::vector<unsigned char> data;
unsigned int pos;
public:
VirtualFileOnMemory(std::vector<unsigned char> data) : data(std::move(data)), pos(0) {
}
~VirtualFileOnMemory() override {
}
unsigned int tell() const override {
return pos;
}
bool seek(unsigned int dis, const SeekType seekType) override {
unsigned int ptr = 0;
switch (seekType) {
case BEGIN:
ptr = dis;
break;
case CURRENT:
ptr = pos + dis;
break;
case END:
ptr = data.size() + dis;
break;
default:
return false;
}
if (ptr < 0) {
::SetLastError(ERROR_NEGATIVE_SEEK);
return false;
}
if (ptr > data.size()) {
ptr = data.size();
}
pos = ptr;
return true;
}
unsigned int read(unsigned char * const buf, const unsigned int size) override {
unsigned int readSize = size;
if (data.size() < pos + size) {
readSize = data.size() - pos;
::SetLastError(ERROR_HANDLE_EOF);
}
std::copy(
data.begin() + pos,
data.begin() + pos + readSize,
stdext::checked_array_iterator<unsigned char *>(buf, readSize));
pos += readSize;
return readSize;
}
};
NativeFileHandle &getNativeFileHandle(const unsigned int handle) {
assert(getSignature(handle) == SIGNATURE_NATIVE_FILE);
return nativeFileAry[getArg(handle)];
}
VirtualFileHandle &getVirtualFileHandle(const unsigned int handle) {
assert(getSignature(handle) == SIGNATURE_VIRTUAL_FILE);
return virtualFileAry[getArg(handle)];
}
unsigned int hookCreateFileW(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition);
HANDLE __stdcall d_CreateFileW(
const wchar_t * const lpFileName,
const DWORD dwDesiredAccess,
const DWORD dwShareMode,
SECURITY_ATTRIBUTES * const lpSecurityAttributes,
const DWORD dwCreationDisposition,
const DWORD dwFlagsAndAttributes,
const HANDLE hTemplateFile)
{
const unsigned int virtualHandle = hookCreateFileW(lpFileName, dwDesiredAccess, dwCreationDisposition);
if (reinterpret_cast<HANDLE>(virtualHandle) != INVALID_HANDLE_VALUE) {
return reinterpret_cast<HANDLE>(virtualHandle);
}
const HANDLE rawHandle = ::CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
if (rawHandle == INVALID_HANDLE_VALUE) {
return INVALID_HANDLE_VALUE;
}
const unsigned int result = newNativeFileHandle();
NativeFileHandle &file = getNativeFileHandle(result);
file.reset(rawHandle);
return reinterpret_cast<HANDLE>(result);
}
BOOL __stdcall d_CloseHandle(const HANDLE rawHandle) {
const unsigned int handle = reinterpret_cast<unsigned int>(rawHandle);
if (isKnownHandle(handle)) {
closeHandle(handle);
return TRUE;
}
return ::CloseHandle(rawHandle);
}
BOOL __stdcall d_ReadFile(const HANDLE hFile, void * const lpBuffer, const DWORD nNumberOfBytesToRead, DWORD * const lpNumberOfBytesRead, OVERLAPPED * const lpOverlapped) {
const unsigned int handle = reinterpret_cast<unsigned int>(hFile);
if (isNativeFileHandle(handle)) {
return ::ReadFile(getNativeFileHandle(handle).get(), lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
}
assert(isVirtualFileHandle(handle));
VirtualFileHandle &file = getVirtualFileHandle(handle);
if (lpBuffer == nullptr) {
return FALSE;
}
const unsigned int readSize = file->read(reinterpret_cast<unsigned char *>(lpBuffer), nNumberOfBytesToRead);
if (lpNumberOfBytesRead != nullptr) {
*lpNumberOfBytesRead = readSize;
}
return TRUE;
}
DWORD __stdcall d_SetFilePointer(const HANDLE hFile, const LONG lDistanceToMove, LONG * const lpDistanceToMoveHigh, const DWORD dwMoveMethod) {
const unsigned int handle = reinterpret_cast<unsigned int>(hFile);
if (isNativeFileHandle(handle)) {
return ::SetFilePointer(getNativeFileHandle(handle).get(), lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod);
}
assert(isVirtualFileHandle(handle));
VirtualFileHandle &file = getVirtualFileHandle(handle);
long long int distance = 0;
if (lpDistanceToMoveHigh == nullptr) {
distance = lDistanceToMove;
} else {
distance = static_cast<unsigned int>(lDistanceToMove) | (static_cast<long long int>(*lpDistanceToMoveHigh) << 32);
}
unsigned long long int ptr = 0;
VirtualFileBase::SeekType type;
switch (dwMoveMethod) {
case FILE_BEGIN:
type = VirtualFileBase::BEGIN;
break;
case FILE_CURRENT:
type = VirtualFileBase::CURRENT;
break;
case FILE_END:
type = VirtualFileBase::END;
break;
default:
return FALSE;
}
return file->seek(static_cast<unsigned int>(distance), type) ? TRUE : FALSE;
}
DWORD __stdcall d_GetFileType(HANDLE hFile) {
const unsigned int handle = reinterpret_cast<unsigned int>(hFile);
if (isNativeFileHandle(handle)) {
return ::GetFileType(getNativeFileHandle(handle).get());
}
if (isVirtualFileHandle(handle)) {
return FILE_TYPE_DISK;
}
return ::GetFileType(hFile);
}
BOOL __stdcall d_WriteFile(
const HANDLE hFile,
const void * const lpBuffer,
const DWORD nNumberOfBytesToWrite,
DWORD * const lpNumberOfBytesWritten,
OVERLAPPED * const lpOverlapped)
{
const unsigned int handle = reinterpret_cast<unsigned int>(hFile);
if (isNativeFileHandle(handle)) {
return ::WriteFile(getNativeFileHandle(handle).get(), lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}
assert(isVirtualFileHandle(handle));
assert(false);
return FALSE;
}
bool hookPathFileExistsW(const wchar_t * const pszPath);
BOOL __stdcall d_PathFileExistsW(const wchar_t * const pszPath) {
return ::PathFileExistsW(pszPath) || hookPathFileExistsW(pszPath);
}
// ダミー実装ここまで
// 独自機能ここから
const wchar_t LIST_DIR_NAME[] = L"list";
const wchar_t TH135_LIST_FILENAME[] = L"135.txt";
const wchar_t TH145_LIST_FILENAME[] = L"145.txt";
const wchar_t TH155_LIST_FILENAME[] = L"155.txt";
struct MusicInfo {
const char * const path;
const unsigned int headerSize;
const unsigned int introSize;
const unsigned int bodySize;
const char * const name;
};
const MusicInfo th135MusicList[] = {
{ "data/bgm/reimu1.ogg", 0x00000000, 0x0004927c * 4, 0x00461595 * 4, "春色小径 〜 Colorful Path" },
{ "data/bgm/marisa1.ogg", 0x00000000, 0x0003ca1d * 4, 0x0050bfe0 * 4, "メイガスナイト" },
{ "data/bgm/ichirin1.ogg", 0x00000000, 0x000159cc * 4, 0x0046e14c * 4, "時代親父とハイカラ少女" },
{ "data/bgm/hijiri1.ogg", 0x00000000, 0x00098cee * 4, 0x00546b7c * 4, "感情の摩天楼 〜 Cosmic Mind" },
{ "data/bgm/futo1.ogg", 0x00000000, 0x00031a05 * 4, 0x005237bf * 4, "大神神話伝" },
{ "data/bgm/miko1.ogg", 0x00000000, 0x001999a3 * 4, 0x004fcf7e * 4, "聖徳伝説 〜 True Administrator" },
{ "data/bgm/nitori1.ogg", 0x00000000, 0x00074443 * 4, 0x004ae670 * 4, "芥川龍之介の河童 〜 Candid Friend" },
{ "data/bgm/koishi1.ogg", 0x00000000, 0x0003e4d4 * 4, 0x004f5719 * 4, "ハルトマンの妖怪少女" },
{ "data/bgm/mamizou1.ogg", 0x00000000, 0x000367dc * 4, 0x0050c0a8 * 4, "佐渡のニッ岩" },
{ "data/bgm/chuboss1.ogg", 0x00000000, 0x00097c77 * 4, 0x004c6c8c * 4, "幻想郷の二ッ岩" },
{ "data/bgm/kokoro1.ogg", 0x00000000, 0x0007b749 * 4, 0x00a8b328 * 4, "亡失のエモーション" },
{ "data/bgm/op.ogg", 0x00000000, 0x00000000 * 4, 0x001fd6b2 * 4, "塵界不変のペシミズム" },
{ "data/bgm/title.ogg", 0x00000000, 0x00030600 * 4, 0x00409982 * 4, "心綺楼囃子" },
{ "data/bgm/ed.ogg", 0x00000000, 0x00025519 * 4, 0x00307320 * 4, "暁雲" },
{ "data/bgm/staffroll.ogg", 0x00000000, 0x00000000 * 4, 0x003053f2 * 4, "官板黄昏新聞" },
{ "data/bgm/win.ogg", 0x00000000, 0x0006bcae * 4, 0x000e5af3 * 4, "本日の一面記事" },
//{ "data/bgm/staffroll2.ogg", 0x00000000, 0x00000000 * 4, 0x0036162b * 4, "" },
{ "data/bgm/kaiwa1.ogg", 0x00000000, 0x0001d86e * 4, 0x002b1101 * 4, "人気のある場所" },
{ "data/bgm/kaiwa3.ogg", 0x00000000, 0x0001e847 * 4, 0x002b10fe * 4, "人気のない場所" },
//{ "data/bgm/kaiwa4.ogg", 0x00000000, 0x00037aa4 * 4, 0x000e5b13 * 4, "" },
{ "data/bgm/kaiwa5.ogg", 0x00000000, 0x0006219a * 4, 0x002241e1 * 4, "丑三つ時の里" },
{ "data/bgm/charasel.ogg", 0x00000000, 0x0005027f * 4, 0x003126e4 * 4, "演者選択" },
{ "data/bgm/reimu2.ogg", 0x00000000, 0x00000000 * 4, 0x000e7742 * 4, "人気爆発/博麗霊夢" },
{ "data/bgm/marisa2.ogg", 0x00000000, 0x00000000 * 4, 0x000e7780 * 4, "人気爆発/霧雨魔理沙" },
{ "data/bgm/ichirin2.ogg", 0x00000000, 0x00000000 * 4, 0x000e779b * 4, "人気爆発/雲居一輪&雲山" },
{ "data/bgm/hijiri2.ogg", 0x00000000, 0x00000000 * 4, 0x000e778d * 4, "人気爆発/聖白蓮" },
{ "data/bgm/futo2.ogg", 0x00000000, 0x00000000 * 4, 0x000e778c * 4, "人気爆発/物部布都" },
{ "data/bgm/miko2.ogg", 0x00000000, 0x00000000 * 4, 0x000e778a * 4, "人気爆発/豊聡耳神子" },
{ "data/bgm/nitori2.ogg", 0x00000000, 0x00000000 * 4, 0x000e7797 * 4, "人気爆発/河城にとり" },
{ "data/bgm/koishi2.ogg", 0x00000000, 0x00000000 * 4, 0x000e7769 * 4, "人気爆発/古明地こいし" },
{ "data/bgm/mamizou2.ogg", 0x00000000, 0x00000000 * 4, 0x000e777b * 4, "人気爆発/二ッ岩マミゾウ" },
{ "data/bgm/kokoro2.ogg", 0x00000000, 0x00000000 * 4, 0x000e777f * 4, "人気爆発/秦こころ" },
{ "data/bgm/maxwaza.ogg", 0x00000000, 0x00029679 * 4, 0x001c2b1d * 4, "ラストワード発動" },
};
const MusicInfo th145MusicList[] = {
// オープニング曲
{ "data/bgm/title.ogg", 0x00000000, 0x000395FD * 4, 0x003C8FE8 * 4, "心揺さぶる都市伝説" },
{ "data/bgm/select.ogg", 0x00000000, 0x0003F3C4 * 4, 0x001E6D6B * 4, "幻想郷ふしぎ発見" },
// 各キャラテーマ曲
{ "data/bgm/reimu1.ogg", 0x00000000, 0x0003992E * 4, 0x0059B8C0 * 4, "二色蓮花蝶 〜 Red and White" },
{ "data/bgm/marisa1.ogg", 0x00000000, 0x000938DA * 4, 0x005EFFDB * 4, "恋色マスタースパーク" },
{ "data/bgm/kasen1.ogg", 0x00000000, 0x0003CCF5 * 4, 0x0062CCA8 * 4, "華狭間のバトルフィールド" },
{ "data/bgm/ichirin1.ogg", 0x00000000, 0x00026C37 * 4, 0x0051270B * 4, "時代親父とハイカラ少女" },
{ "data/bgm/hijiri1.ogg", 0x00000000, 0x000C92B6 * 4, 0x0057ED40 * 4, "感情の摩天楼 〜 Cosmic Mind" },
{ "data/bgm/futo1.ogg", 0x00000000, 0x00034F05 * 4, 0x005D2C0A * 4, "大神神話伝" },
{ "data/bgm/miko1.ogg", 0x00000000, 0x00103560 * 4, 0x005CB65F * 4, "聖徳伝説 〜 True Administrator" },
{ "data/bgm/nitori1.ogg", 0x00000000, 0x00013B8A * 4, 0x0058A479 * 4, "芥川龍之介の河童 〜 Candid Friend" },
{ "data/bgm/koishi1.ogg", 0x00000000, 0x00018BD3 * 4, 0x004FECEF * 4, "ハルトマンの妖怪少女" },
{ "data/bgm/mamizou1.ogg", 0x00000000, 0x00019D1E * 4, 0x004AD74C * 4, "幻想郷の二ッ岩" },
{ "data/bgm/kokoro1.ogg", 0x00000000, 0x00094025 * 4, 0x00975901 * 4, "亡失のエモーション" },
{ "data/bgm/mokou1.ogg", 0x00000000, 0x00071113 * 4, 0x006EBE00 * 4, "月まで届け、不死の煙" },
{ "data/bgm/sinmyoumaru1.ogg", 0x00000000, 0x000B5D99 * 4, 0x00595E72 * 4, "輝く針の小人族 〜 Little Princess" },
{ "data/bgm/usami1.ogg", 0x00000000, 0x000B88A5 * 4, 0x009A4D6F * 4, "ラストオカルティズム 〜 現し世の秘術師" },
// 通常戦闘曲
{ "data/bgm/st01_1.ogg", 0x00000000, 0x0004CA93 * 4, 0x004B30D4 * 4, "七玉蒐集ショウダウン" },
{ "data/bgm/st02_1.ogg", 0x00000000, 0x00059209 * 4, 0x00435E3E * 4, "オカルトアラカルト" },
{ "data/bgm/st03_1.ogg", 0x00000000, 0x0007FEFF * 4, 0x003CCFE3 * 4, "公正なる奪い合い" },
{ "data/bgm/st04_1.ogg", 0x00000000, 0x0009CFD4 * 4, 0x00405D9A * 4, "対蹠地の鐘" },
{ "data/bgm/st05_1.ogg", 0x00000000, 0x0006BC35 * 4, 0x0040997C * 4, "竹林インフレイム" },
// 各キャラテーマ曲、キャラセレクト時の20秒版
/*
{ "data/bgm/reimu2.ogg", 0x00000000, 0x00000000 * 4, 0x000E5E9C * 4, "二色蓮花蝶 〜 Red and White" },
{ "data/bgm/marisa2.ogg", 0x00000000, 0x00000000 * 4, 0x000EAD74 * 4, "恋色マスタースパーク" },
{ "data/bgm/kasen1.ogg", 0x00000000, 0x0003CCF5 * 4, 0x0062CCA8 * 4, "華狭間のバトルフィールド" },
{ "data/bgm/ichirin2.ogg", 0x00000000, 0x00000000 * 4, 0x000D929A * 4, "時代親父とハイカラ少女" },
{ "data/bgm/hijiri2.ogg", 0x00000000, 0x00000000 * 4, 0x000DAFB6 * 4, "感情の摩天楼 〜 Cosmic Mind" },
{ "data/bgm/futo2.ogg", 0x00000000, 0x00000000 * 4, 0x000DF950 * 4, "大神神話伝" },
{ "data/bgm/miko2.ogg", 0x00000000, 0x00000000 * 4, 0x000FF2F5 * 4, "聖徳伝説 〜 True Administrator" },
{ "data/bgm/nitori2.ogg", 0x00000000, 0x00000000 * 4, 0x000EB0E3 * 4, "芥川龍之介の河童 〜 Candid Friend" },
{ "data/bgm/koishi2.ogg", 0x00000000, 0x00000000 * 4, 0x000F1C1B * 4, "ハルトマンの妖怪少女" },
{ "data/bgm/mamizou2.ogg", 0x00000000, 0x00000000 * 4, 0x000FC368 * 4, "幻想郷の二ッ岩" },
{ "data/bgm/kokoro2.ogg", 0x00000000, 0x00000000 * 4, 0x000EC32A * 4, "亡失のエモーション" },
{ "data/bgm/mokou2.ogg", 0x00000000, 0x00000000 * 4, 0x000DD890 * 4, "月まで届け、不死の煙" },
{ "data/bgm/sinmyoumaru2.ogg", 0x00000000, 0x00000000 * 4, 0x000E8470 * 4, "輝く針の小人族 〜 Little Princess" },
{ "data/bgm/usami2.ogg", 0x00000000, 0x00000000 * 4, 0x000F33E8 * 4, "ラストオカルティズム 〜 現し世の秘術師" },
*/
// 通常戦闘曲、キャラセレクト時の20秒版
/*
{ "data/bgm/st01_2.ogg", 0x00000000, 0x00000000 * 4, 0x000F826D * 4, "七玉蒐集ショウダウン" },
{ "data/bgm/st02_2.ogg", 0x00000000, 0x00000000 * 4, 0x000EC74A * 4, "オカルトアラカルト" },
{ "data/bgm/st03_2.ogg", 0x00000000, 0x00000000 * 4, 0x000DF578 * 4, "公正なる奪い合い" },
{ "data/bgm/st04_2.ogg", 0x00000000, 0x00000000 * 4, 0x000E098E * 4, "対蹠地の鐘" },
{ "data/bgm/st05_2.ogg", 0x00000000, 0x00000000 * 4, 0x000EAFF2 * 4, "竹林インフレイム" },
*/
// ストーリー曲
{ "data/bgm/ta01.ogg", 0x00000000, 0x000564C4 * 4, 0x002B1100 * 4, "顕現した伝承の形" },
{ "data/bgm/ta02.ogg", 0x00000000, 0x0008D313 * 4, 0x00233C76 * 4, "ボールのある日常" },
{ "data/bgm/ta03.ogg", 0x00000000, 0x00016C4A * 4, 0x0026C280 * 4, "価値がわからない" },
{ "data/bgm/ta04.ogg", 0x00000000, 0x00033697 * 4, 0x00204CB6 * 4, "時代の風の訪れ" },
{ "data/bgm/ta05.ogg", 0x00000000, 0x00084278 * 4, 0x00278D01 * 4, "真実を知る者" },
{ "data/bgm/ta06.ogg", 0x00000000, 0x0004FE5C * 4, 0x002D6BF0 * 4, "可能性を信じて" },
{ "data/bgm/ta07.ogg", 0x00000000, 0x00025445 * 4, 0x0027389D * 4, "外界フォークロア" },
// 特殊演出用曲
/*
{ "data/bgm/ta13.ogg", 0x00000000, 0x00000000 * 4, 0x001274FF * 4, "SE" },
{ "data/bgm/ta14.ogg", 0x00000000, 0x0001EFE6 * 4, 0x00081330 * 4, "SE" },
*/
// エンディング曲
{ "data/bgm/ed.ogg", 0x00000000, 0x0004994A * 4, 0x00260E57 * 4, "各々の結末" },
{ "data/bgm/staffroll.ogg", 0x00000000, 0x00000000 * 4, 0x00397B22 * 4, "明かされる深秘" },
};
const MusicInfo th155MusicList[] = {
// 並び順はMusic Room準拠
// 各キャラテーマ曲
{ "data/bgm/155reimu1.ogg", 0x00000000, 0x0003992E * 4, 0x0059B8C0 * 4, "二色蓮花蝶 〜 Red and White" },
{ "data/bgm/155marisa1.ogg", 0x00000000, 0x000938DA * 4, 0x005EFFDB * 4, "恋色マスタースパーク" },
{ "data/bgm/155ichirin1.ogg", 0x00000000, 0x00026C37 * 4, 0x0051270B * 4, "時代親父とハイカラ少女" },
{ "data/bgm/155hijiri1.ogg", 0x00000000, 0x000C92B6 * 4, 0x0057ED40 * 4, "感情の摩天楼 〜 Cosmic Mind" },
{ "data/bgm/155futo1.ogg", 0x00000000, 0x00034F05 * 4, 0x005D2C0A * 4, "大神神話伝" },
{ "data/bgm/155miko1.ogg", 0x00000000, 0x00103560 * 4, 0x005CB65F * 4, "聖徳伝説 〜 True Administrator" },
{ "data/bgm/155nitori1.ogg", 0x00000000, 0x00013B8A * 4, 0x0058A479 * 4, "芥川龍之介の河童 〜 Candid Friend" },
{ "data/bgm/155koishi1.ogg", 0x00000000, 0x00018BD3 * 4, 0x004FECEF * 4, "ハルトマンの妖怪少女" },
{ "data/bgm/155mamizou1.ogg", 0x00000000, 0x00019D1E * 4, 0x004AD74C * 4, "幻想郷の二ッ岩" },
{ "data/bgm/155kokoro1.ogg", 0x00000000, 0x00093D07 * 4, 0x009758BC * 4, "亡失のエモーション" },
{ "data/bgm/155mokou1.ogg", 0x00000000, 0x00071113 * 4, 0x006EBE00 * 4, "月まで届け、不死の煙" },
{ "data/bgm/155sinmyoumaru1.ogg", 0x00000000, 0x0003A481 * 4, 0x005B13F5 * 4, "輝く針の小人族 〜 Little Princess" },
{ "data/bgm/155kasen1.ogg", 0x00000000, 0x0008D9C1 * 4, 0x00565DEB * 4, "華狭間のバトルフィールド" },
{ "data/bgm/155usami1.ogg", 0x00000000, 0x0007A6A4 * 4, 0x00534CAD * 4, "ラストオカルティズム 〜 現し世の秘術師" },
{ "data/bgm/155udonge1.ogg", 0x00000000, 0x001758D1 * 4, 0x0050C003 * 4, "狂気の瞳 〜 Invisible Full Moon" },
{ "data/bgm/155doremy1.ogg", 0x00000000, 0x000D7575 * 4, 0x006DBC8C * 4, "永遠の春夢" },
{ "data/bgm/155tenshi1.ogg", 0x00000000, 0x0002866D * 4, 0x006D0497 * 4, "有頂天変 〜 Wonderful Heaven" },
{ "data/bgm/155yukari1.ogg", 0x00000000, 0x0003AA3E * 4, 0x00552FE9 * 4, "夜が降りてくる 〜 Evening Star" },
// ストーリー戦闘曲
{ "data/bgm/bgst01.ogg", 0x00000000, 0x0005A9DA * 4, 0x003A51B5 * 4, "地の色は黄色 〜 Primrose" },
{ "data/bgm/bgst02.ogg", 0x00000000, 0x0006A2B3 * 4, 0x00412367 * 4, "マッシュルーム・ワルツ" },
{ "data/bgm/bgst03.ogg", 0x00000000, 0x00060EBE * 4, 0x0042321F * 4, "聖輦船空を往く" },
{ "data/bgm/bgst04.ogg", 0x00000000, 0x00044DED * 4, 0x003E65CA * 4, "法力の下の平等" },
{ "data/bgm/bgst05.ogg", 0x00000000, 0x0008325D * 4, 0x003C8FE4 * 4, "恒常不変の参廟祀" },
{ "data/bgm/bgst06.ogg", 0x00000000, 0x0007BAEF * 4, 0x00384FEA * 4, "光輝く天球儀" },
{ "data/bgm/bgst07.ogg", 0x00000000, 0x0002DC73 * 4, 0x003CDAD1 * 4, "沢の河童の技術力" },
{ "data/bgm/bgst08.ogg", 0x00000000, 0x00053C13 * 4, 0x003D369E * 4, "地底に咲く薔薇" },
{ "data/bgm/bgst09.ogg", 0x00000000, 0x000532A4 * 4, 0x003E9C35 * 4, "深緑の狸森にて" },
{ "data/bgm/bgst10.ogg", 0x00000000, 0x00026682 * 4, 0x003C4197 * 4, "心綺楼演舞" },
{ "data/bgm/bgst11.ogg", 0x00000000, 0x000A7C29 * 4, 0x0040997C * 4, "不滅のレッドソウル" },
{ "data/bgm/bgst12.ogg", 0x00000000, 0x00027AD2 * 4, 0x003D62C2 * 4, "落日に映える逆さ城" },
{ "data/bgm/bgst13.ogg", 0x00000000, 0x001181DD * 4, 0x003D1B91 * 4, "千の試練を超えて" },
{ "data/bgm/bgst14.ogg", 0x00000000, 0x00074896 * 4, 0x0043A7AA * 4, "夢世界フォークロア" },
{ "data/bgm/bgst15.ogg", 0x00000000, 0x000BC905 * 4, 0x003C8FE8 * 4, "永遠に続く回廊" },
{ "data/bgm/bgst16.ogg", 0x00000000, 0x00012E1F * 4, 0x003BAF3A * 4, "スリープシープ・パレード" },
{ "data/bgm/bgst17.ogg", 0x00000000, 0x0008C6D5 * 4, 0x003CDAD1 * 4, "至る有頂天" },
// 中ボス曲
{ "data/bgm/midboss.ogg", 0x00000000, 0x0007E036 * 4, 0x005C036D * 4, "憑坐は夢と現の間に 〜 Necro-Fantasia" },
// ラスボス曲
{ "data/bgm/lastboss.ogg", 0x00000000, 0x000B7CB3 * 4, 0x009D1732 * 4, "今宵は飄逸なエゴイスト(Live ver) 〜 Egoistic Flowers." },
// 深秘録から続投
{ "data/bgm/145st01.ogg", 0x00000000, 0x0003B84F * 4, 0x00435E4E * 4, "オカルトアトラクト" },
{ "data/bgm/145st02.ogg", 0x00000000, 0x0001C71B * 4, 0x003DD2E9 * 4, "ネオ竹林インフレイム" },
{ "data/bgm/145st03.ogg", 0x00000000, 0x0003B262 * 4, 0x00434A8A * 4, "億万劫の鐘" },
{ "data/bgm/145st04.ogg", 0x00000000, 0x001D5D95 * 4, 0x0067B098 * 4, "アンノウンX 〜 Occultly Madness" },
// ストーリー会話パート曲
{ "data/bgm/story01.ogg", 0x00000000, 0x00032C96 * 4, 0x00292A99 * 4, "憑依投合" },
{ "data/bgm/story02.ogg", 0x00000000, 0x0005D9A3 * 4, 0x002AE5EF * 4, "連帯責人" },
{ "data/bgm/story03.ogg", 0x00000000, 0x00020585 * 4, 0x0029FD60 * 4, "合縁奇縁" },
{ "data/bgm/story04.ogg", 0x00000000, 0x000144C4 * 4, 0x002AE5EF * 4, "異心同体" },
{ "data/bgm/story05.ogg", 0x00000000, 0x00026EAA * 4, 0x0029FD5F * 4, "壮言大語" },
{ "data/bgm/story06.ogg", 0x00000000, 0x0002EDA5 * 4, 0x002A64BA * 4, "知略縦横" },
{ "data/bgm/story07.ogg", 0x00000000, 0x0002EC31 * 4, 0x002DC20D * 4, "意気揚々" },
{ "data/bgm/story08.ogg", 0x00000000, 0x0004F4BC * 4, 0x00299922 * 4, "開演間近" },
{ "data/bgm/story09.ogg", 0x00000000, 0x00071759 * 4, 0x002B10FC * 4, "天衣無縫 〜 Yellow Lily" },
// エンディングでも使われてる曲
{ "data/bgm/ending.ogg", 0x00000000, 0x000397EB * 4, 0x002F0D42 * 4, "行雲流水" },
// オープニング/エンディング曲
{ "data/bgm/th155op1.ogg", 0x00000000, 0x0008149C * 4, 0x003277EC * 4, "異変の種子" },
{ "data/bgm/th155op2.ogg", 0x00000000, 0x000814CE * 4, 0x003277ED * 4, "疑惑の芽生え" },
{ "data/bgm/staffroll0.ogg", 0x00000000, 0x00000000 * 4, 0x003A308B * 4, "未だ蕾む憑依華" },
{ "data/bgm/th155op3.ogg", 0x00000000, 0x000814C7 * 4, 0x003277EC * 4, "真相へ繋がる枝葉" },
{ "data/bgm/staffroll1.ogg", 0x00000000, 0x00000000 * 4, 0x00444AC8 * 4, "咲き誇る憑依華" },
{ "data/bgm/th155op4.ogg", 0x00000000, 0x000814D8 * 4, 0x003277EC * 4, "舞い散る憑依華吹雪" },
{ "data/bgm/staffroll2.ogg", 0x00000000, 0x00000000 * 4, 0x0041E685 * 4, "悠久の蒸気機関" },
// 20秒版
/*
{ "data/bgm/155reimu1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000E5E9C * 4, "二色蓮花蝶 〜 Red and White" },
{ "data/bgm/155marisa1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000EAD74 * 4, "恋色マスタースパーク" },
{ "data/bgm/155ichirin1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D929A * 4, "時代親父とハイカラ少女" },
{ "data/bgm/155hijiri1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000DAFB6 * 4, "感情の摩天楼 〜 Cosmic Mind" },
{ "data/bgm/155futo1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000DF950 * 4, "大神神話伝" },
{ "data/bgm/155miko1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000FF2F5 * 4, "聖徳伝説 〜 True Administrator" },
{ "data/bgm/155nitori1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000EB0E3 * 4, "芥川龍之介の河童 〜 Candid Friend" },
{ "data/bgm/155koishi1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000F1C1B * 4, "ハルトマンの妖怪少女" },
{ "data/bgm/155mamizou1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000FC368 * 4, "幻想郷の二ッ岩" },
{ "data/bgm/155kokoro1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000EC5CF * 4, "亡失のエモーション" },
{ "data/bgm/155mokou1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000DD890 * 4, "月まで届け、不死の煙" },
{ "data/bgm/155sinmyoumaru1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "輝く針の小人族 〜 Little Princess" },
{ "data/bgm/155kasen1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7551 * 4, "華狭間のバトルフィールド" },
{ "data/bgm/155usami1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "ラストオカルティズム 〜 現し世の秘術師" },
{ "data/bgm/155udonge1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D5166 * 4, "狂気の瞳 〜 Invisible Full Moon" },
{ "data/bgm/155doremy1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "永遠の春夢" },
{ "data/bgm/155tenshi1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000F7A1C * 4, "有頂天変 〜 Wonderful Heaven" },
{ "data/bgm/155yukari1_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "夜が降りてくる 〜 Evening Star" },
{ "data/bgm/bgst01_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "地の色は黄色 〜 Primrose" },
{ "data/bgm/bgst02_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "マッシュルーム・ワルツ" },
{ "data/bgm/bgst03_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "聖輦船空を往く" },
{ "data/bgm/bgst04_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "法力の下の平等" },
{ "data/bgm/bgst05_20.ogg", 0x00000000, 0x00000000 * 4, 0x000EB93A * 4, "恒常不変の参廟祀" },
{ "data/bgm/bgst06_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "光輝く天球儀" },
{ "data/bgm/bgst07_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "沢の河童の技術力" },
{ "data/bgm/bgst08_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "地底に咲く薔薇" },
{ "data/bgm/bgst09_20.ogg", 0x00000000, 0x00000000 * 4, 0x000E547A * 4, "深緑の狸森にて" },
{ "data/bgm/bgst10_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "心綺楼演舞" },
{ "data/bgm/bgst11_20.ogg", 0x00000000, 0x00000000 * 4, 0x000FB54C * 4, "不滅のレッドソウル" },
{ "data/bgm/bgst12_20.ogg", 0x00000000, 0x00000000 * 4, 0x000E5920 * 4, "落日に映える逆さ城" },
{ "data/bgm/bgst13_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "千の試練を超えて" },
{ "data/bgm/bgst14_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "夢世界フォークロア" },
{ "data/bgm/bgst15_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "永遠に続く回廊" },
{ "data/bgm/bgst16_20.ogg", 0x00000000, 0x00000000 * 4, 0x000E08B9 * 4, "スリープシープ・パレード" },
{ "data/bgm/bgst17_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "至る有頂天" },
{ "data/bgm/midboss_20.ogg", 0x00000000, 0x00000000 * 4, 0x000ECDD8 * 4, "憑坐は夢と現の間に 〜 Necro-Fantasia" },
{ "data/bgm/lastboss_20.ogg", 0x00000000, 0x00000000 * 4, 0x000D7550 * 4, "今宵は飄逸なエゴイスト(Live ver) 〜 Egoistic Flowers." },
{ "data/bgm/145st01_20.ogg", 0x00000000, 0x00000000 * 4, 0x000ED752 * 4, "オカルトアトラクト" },
{ "data/bgm/145st02_20.ogg", 0x00000000, 0x00000000 * 4, 0x000DD182 * 4, "ネオ竹林インフレイム" },
{ "data/bgm/145st03_20.ogg", 0x00000000, 0x00000000 * 4, 0x000EAE08 * 4, "億万劫の鐘" },
{ "data/bgm/145st04_20.ogg", 0x00000000, 0x00000000 * 4, 0x000EACEA * 4, "アンノウンX 〜 Occultly Madness" },
*/
};
std::string createMusicInfoString(const MusicInfo * const info, const unsigned int count) {
return count == 0 ? "" : (
(boost::format("\n%%%s,%08x,%08x,%08x,%s") % info[0].path % info[0].headerSize % info[0].introSize % info[0].bodySize % info[0].name).str()
+ createMusicInfoString(&info[1], count - 1)
);
}
std::string createTh135List() {
return std::string("@,東方心綺楼") + createMusicInfoString(th135MusicList, _countof(th135MusicList));
}
std::string createTh145List() {
return std::string("@,東方深秘録") + createMusicInfoString(th145MusicList, _countof(th145MusicList));
}
std::string createTh155List() {
return std::string("@,東方憑依華") + createMusicInfoString(th155MusicList, _countof(th155MusicList));
}
const std::string th135ListTxt = createTh135List();
const std::string th145ListTxt = createTh145List();
const std::string th155ListTxt = createTh155List();
boost::filesystem::path getAppDir() {
wchar_t buf[4096];
::GetModuleFileNameW(NULL, buf, _countof(buf));
const boost::filesystem::path exePath = buf;
boost::filesystem::path exeDir = buf;
exeDir.remove_filename();
return exeDir;
}
boost::filesystem::path getListDir() {
return getAppDir() / LIST_DIR_NAME;
}
boost::filesystem::path getTh135ListPath() {
return getListDir() / TH135_LIST_FILENAME;
}
boost::filesystem::path getTh145ListPath() {
return getListDir() / TH145_LIST_FILENAME;
}
boost::filesystem::path getTh155ListPath() {
return getListDir() / TH155_LIST_FILENAME;
}
void hookFindFirstFileW(const wchar_t * const lpFileName, std::vector<WIN32_FIND_DATAW> &data) {
const boost::filesystem::path targetPath = getListDir() / L"*.txt";
if (targetPath != lpFileName) {
return;
}
struct {
const wchar_t * const fileName;
const unsigned int fileSize;
} list[] = {
#ifdef ENABLE_TH135
{ TH135_LIST_FILENAME, th135ListTxt.size() },
#endif
#ifdef ENABLE_TH145
{ TH145_LIST_FILENAME, th145ListTxt.size() },
#endif
#ifdef ENABLE_TH155
{ TH155_LIST_FILENAME, th155ListTxt.size() },
#endif
};
data.resize(data.size() + _countof(list));
auto it = data.end() - _countof(list);
SYSTEMTIME st;
::GetSystemTime(&st);
for (const auto &item : list) {
WIN32_FIND_DATAW &fd = *it;
fd.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
::SystemTimeToFileTime(&st, &fd.ftCreationTime);
::SystemTimeToFileTime(&st, &fd.ftLastAccessTime);
::SystemTimeToFileTime(&st, &fd.ftLastWriteTime);
fd.nFileSizeLow = item.fileSize;
::wcscpy_s(fd.cFileName, _countof(fd.cFileName), item.fileName);
it++;
}
}
unsigned int hookCreateFileWTh135List(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition) {
const boost::filesystem::path listPath = getTh135ListPath();
if (listPath != lpFileName) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const unsigned int handle = newVirtualFileHandle();
VirtualFileHandle &file = getVirtualFileHandle(handle);
file = std::make_unique<VirtualFileOnMemory>(
std::vector<unsigned char>(th135ListTxt.begin(), th135ListTxt.end())
);
return handle;
}
unsigned int hookCreateFileWTh145List(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition) {
const boost::filesystem::path listPath = getTh145ListPath();
if (listPath != lpFileName) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const unsigned int handle = newVirtualFileHandle();
VirtualFileHandle &file = getVirtualFileHandle(handle);
file = std::make_unique<VirtualFileOnMemory>(
std::vector<unsigned char>(th145ListTxt.begin(), th145ListTxt.end())
);
return handle;
}
unsigned int hookCreateFileWTh155List(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition) {
const boost::filesystem::path listPath = getTh155ListPath();
if (listPath != lpFileName) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const unsigned int handle = newVirtualFileHandle();
VirtualFileHandle &file = getVirtualFileHandle(handle);
file = std::make_unique<VirtualFileOnMemory>(
std::vector<unsigned char>(th155ListTxt.begin(), th155ListTxt.end())
);
return handle;
}
class VirtualWavFile : public VirtualFileBase {
private:
OggVorbis_File ovf;
const unsigned int blockSize;
std::vector<unsigned char> temp;
public:
VirtualWavFile(OggVorbis_File ovf) : ovf(ovf), blockSize(4) {
}
~VirtualWavFile() override {
::ov_clear(&ovf);
}
unsigned int size() const {
return static_cast<unsigned int>(::ov_pcm_total(const_cast<OggVorbis_File *>(&ovf), -1)) * blockSize;
}
unsigned int tell() const override {
return static_cast<unsigned int>(::ov_pcm_tell(const_cast<OggVorbis_File *>(&ovf))) * blockSize - temp.size();
}
bool seek(unsigned int dis, const SeekType seekType) override {
assert((dis % blockSize) == 0);
const unsigned int pos = tell();
const unsigned int totalSize = size();
unsigned int ptr = 0;
switch (seekType) {
case BEGIN:
ptr = dis;
break;
case CURRENT:
ptr = pos + dis;
break;
case END:
ptr = totalSize + dis;
break;
default:
return false;
}
if (ptr > totalSize) {
ptr = totalSize;
}
::ov_pcm_seek(&ovf, ptr / blockSize);
temp.clear();
return true;
}
unsigned int read(unsigned char * const buf, const unsigned int bufSize) override {
const unsigned int tempSize = temp.size();
assert((tempSize % 4) == 0);
if (!temp.empty() && bufSize < tempSize) {
std::copy(temp.begin(), temp.begin() + bufSize, stdext::checked_array_iterator<unsigned char *>(buf, bufSize));
std::copy(temp.begin() + bufSize, temp.end(), temp.begin());
temp.resize(tempSize - bufSize);
return bufSize;
}
const unsigned int pos = static_cast<unsigned int>(::ov_pcm_tell(const_cast<OggVorbis_File *>(&ovf))) * blockSize;
const unsigned int totalSize = size();
unsigned int readSize = bufSize - tempSize;
if (pos + readSize > totalSize) {
readSize = totalSize - pos - tempSize;
}
temp.resize(tempSize + (std::max)(bufSize * 2, 4096u));
int bitStream = 0;
unsigned int readSizeResult = 0;
unsigned int tempIndex = tempSize;
while (true) {
const unsigned int curReadSize = ::ov_read(&ovf, reinterpret_cast<char *>(&temp[tempIndex]), temp.size() - tempIndex, 0, 2, 1, &bitStream);
if (curReadSize == 0) {
break;
}
readSizeResult += curReadSize;
tempIndex += curReadSize;
if (tempIndex == temp.size()) {
break;
}
}
temp.resize(tempSize + readSizeResult);
if (readSizeResult < readSize) {
readSize = readSizeResult;
}
std::copy(temp.begin(), temp.begin() + tempSize + readSize, stdext::checked_array_iterator<unsigned char *>(buf, bufSize));
std::copy(temp.begin() + tempSize + readSize, temp.end(), temp.begin());
temp.resize(temp.size() - tempSize - readSize);
if (readSize == 0 && tempSize == 0) {
::SetLastError(ERROR_HANDLE_EOF);
}
return tempSize + readSize;
}
};
unsigned int hookCreateFileWNativeOgg(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition) {
const boost::filesystem::path path = lpFileName;
if (path.extension() != ".ogg" || !boost::filesystem::is_regular_file(path)) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
OggVorbis_File ovf;
if (::ov_fopen(path.string().c_str(), &ovf) != 0) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const unsigned int handle = newVirtualFileHandle();
VirtualFileHandle &file = getVirtualFileHandle(handle);
file = std::make_unique<VirtualWavFile>(ovf);
return handle;
}
template<typename T>
class ReaderBase : public boost::noncopyable {
private:
T &pak;
const TouhouSE::TH135::FileInfo &info;
unsigned int ptr;
public:
ReaderBase(T &pak, const TouhouSE::TH135::FileInfo &info) : pak(pak), info(info), ptr(0) {
seek(0, SEEK_SET);
}
unsigned int size() const {
return info.size;
}
unsigned int tell() const {
return ptr;
}
bool seek(unsigned int dis, const int origin) {
const unsigned int pos = tell();
const unsigned int totalSize = size();
unsigned int ptrPos = 0;
switch (origin) {
case SEEK_SET:
ptrPos = dis;
break;
case SEEK_CUR:
ptrPos = pos + dis;
break;
case SEEK_END:
ptrPos = totalSize + dis;
break;
default:
return false;
}
if (ptrPos > totalSize) {
ptrPos = totalSize;
}
this->ptr = ptrPos;
pak.in->seekg(pak.headerSize + info.address + this->ptr);
return true;
}
unsigned int read(unsigned char * const buf, const unsigned int bufSize) {
const unsigned int pos = tell();
const unsigned int totalSize = size();
unsigned int readSize = bufSize;
if (pos + readSize > totalSize) {
readSize = totalSize - pos;
}
if (readSize == 0) {
return 0;
}
pak.in->read(reinterpret_cast<char *>(buf), readSize);
unsigned char *it = buf;
const unsigned char * const end = &buf[readSize];
for (unsigned int i = (ptr % TouhouSE::TH135::BLOCK_SIZE); i != 0 && buf != end && i < TouhouSE::TH135::BLOCK_SIZE; i++) {
*it ^= info.key[i];
it++;
}
const unsigned int loopCount = (end - it) / TouhouSE::TH135::BLOCK_SIZE;
for (unsigned int i = 0; i < loopCount; i++) {
*reinterpret_cast<unsigned long long int *>(&it[0]) ^= *reinterpret_cast<const unsigned long long int *>(&info.key[0]);
*reinterpret_cast<unsigned long long int *>(&it[8]) ^= *reinterpret_cast<const unsigned long long int *>(&info.key[8]);
it += TouhouSE::TH135::BLOCK_SIZE;
}
for (unsigned int i = 0; it != end; i++) {
*it ^= info.key[i];
it++;
}
assert(it == end);
ptr += readSize;
return readSize;
}
};
class Th135Reader : public ReaderBase<TouhouSE::TH135::Th135Extracter> {
public:
Th135Reader(TouhouSE::TH135::Th135Extracter &pak, const TouhouSE::TH135::FileInfo &info) : ReaderBase<TouhouSE::TH135::Th135Extracter>(pak, info) {
}
};
class Th145Reader : public boost::noncopyable {
private:
TouhouSE::TH145Pak::Th145PakExtracter &pak;
const TouhouSE::TH145Pak::FileInfo &info;
unsigned int ptr;
unsigned int key2;
public:
Th145Reader(TouhouSE::TH145Pak::Th145PakExtracter &pak, const TouhouSE::TH145Pak::FileInfo &info) : pak(pak), info(info), ptr(0) {
seek(0, SEEK_SET);
}
unsigned int size() const {
return info.size;
}
unsigned int tell() const {
return ptr;
}
bool seek(unsigned int dis, const int origin) {
const unsigned int pos = tell();
const unsigned int totalSize = size();
unsigned int ptrPos = 0;
switch (origin) {
case SEEK_SET:
ptrPos = dis;
break;
case SEEK_CUR:
ptrPos = pos + dis;
break;
case SEEK_END:
ptrPos = totalSize + dis;
break;
default:
return false;
}
if (ptrPos > totalSize) {
ptrPos = totalSize;
}
this->ptr = ptrPos;
if (this->ptr == 0) {
pak.in->seekg(info.address);
key2 = *reinterpret_cast<const unsigned int *>(&info.key[0]);
} else {
const unsigned int curPtr = this->ptr - 4;
pak.in->seekg(info.address + curPtr);
std::array<unsigned char, 4> temp;
pak.in->read(reinterpret_cast<char *>(&temp[0]), 4);
switch (curPtr % 4) {
case 0:
key2 = *reinterpret_cast<unsigned int *>(&temp[0]);
break;
case 1:
key2 = (*reinterpret_cast<unsigned int *>(&temp[0]) >> 24) + ((*reinterpret_cast<unsigned int *>(&temp[0]) & 0xFFFFFF) << 8);
break;
case 2:
key2 = (*reinterpret_cast<unsigned int *>(&temp[0]) >> 16) + ((*reinterpret_cast<unsigned int *>(&temp[0]) & 0xFFFF) << 16);
break;
case 3:
key2 = (*reinterpret_cast<unsigned int *>(&temp[0]) >> 8) + ((*reinterpret_cast<unsigned int *>(&temp[0]) & 0xFF) << 24);
break;
}
}
return true;
}
unsigned int read(unsigned char * const buf, const unsigned int bufSize) {
const unsigned int pos = tell();
const unsigned int totalSize = size();
unsigned int readSize = bufSize;
if (pos + readSize > totalSize) {
readSize = totalSize - pos;
}
if (readSize == 0) {
return 0;
}
pak.in->read(reinterpret_cast<char *>(buf), readSize);
const unsigned int pre = (ptr + 3) / 4 * 4 - ptr;
const unsigned int post = (ptr + bufSize) / 4 * 4 - ptr;
for (const unsigned int i : boost::irange(0U, pre)) {
const unsigned int j = ptr + i;
const unsigned char temp = buf[i];
buf[i] = (buf[i] ^ info.key[j % 16]) ^ reinterpret_cast<const unsigned char *>(&key2)[j % 4];
reinterpret_cast<unsigned char *>(&key2)[j % 4] = temp;
}
for (const unsigned int i : boost::irange(pre, post, 4)) {
const unsigned int temp = *reinterpret_cast<const unsigned int *>(&buf[i]);
*reinterpret_cast<unsigned int *>(&buf[i]) = (*reinterpret_cast<const unsigned int *>(&buf[i]) ^ *reinterpret_cast<const unsigned int *>(&info.key[(ptr + i) % 16])) ^ key2;
key2 = temp;
}
for (const unsigned int i : boost::irange(post, bufSize)) {
const unsigned int j = ptr + i;
const unsigned char temp = buf[i];
buf[i] = (buf[i] ^ info.key[j % 16]) ^ reinterpret_cast<const unsigned char *>(&key2)[j % 4];
reinterpret_cast<unsigned char *>(&key2)[j % 4] = temp;
}
ptr += readSize;
return readSize;
}
};
template<typename T>
bool oggOpen(OggVorbis_File &ovf, T * const readerPtr) {
const int result = ::ov_open_callbacks(readerPtr, &ovf, NULL, 0, {
[](void * const buf, const size_t size, const size_t n, void * const ptr) -> size_t {
T &reader = *reinterpret_cast<T *>(ptr);
return reader.read(reinterpret_cast<unsigned char *>(buf), size * n) / size;
},
[](void * const ptr, const ogg_int64_t dis, const int origin) -> int {
T &reader = *reinterpret_cast<T *>(ptr);
return reader.seek(static_cast<unsigned int>(dis), origin) ? 0 : 1;
},
[](void * const ptr) -> int {
T * const reader = reinterpret_cast<T *>(ptr);
delete ptr;
return 0;
},
[](void * const ptr) -> long {
const T &reader = *reinterpret_cast<T *>(ptr);
return reader.tell();
}
});
return result == 0;
}
unsigned int getRelativeRootPos(const wchar_t * const lpFileName) {
boost::filesystem::path tempPath = lpFileName;
while (true) {
if (tempPath.empty()) {
return{};
}
if (tempPath.filename() == L"data") {
tempPath = tempPath.parent_path();
break;
}
tempPath = tempPath.parent_path();
}
return tempPath.wstring().size() + 1;
}
boost::filesystem::path getRelativeRootPath(const wchar_t * const lpFileName) {
return boost::filesystem::path(&lpFileName[0], &lpFileName[getRelativeRootPos(lpFileName)]);
}
boost::filesystem::path getRelativePath(const wchar_t * const lpFileName) {
return boost::filesystem::path(&lpFileName[getRelativeRootPos(lpFileName)], &lpFileName[wcslen(lpFileName)]);
}
boost::shared_ptr<TouhouSE::TH135::Th135Extracter> th135pak = nullptr;
boost::shared_ptr<TouhouSE::TH135::Th135Extracter> th135pakB = nullptr;
unsigned int hookCreateFileWTh135Ogg(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition) {
if (!th135pak || !th135pakB || !boost::filesystem::is_regular_file(getRelativeRootPath(lpFileName) / L"th135.pak")) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const boost::filesystem::path path = getRelativePath(lpFileName);
const TouhouSE::TH135::FileInfo * const info = th135pak->Find(path);
const TouhouSE::TH135::FileInfo * const infoB = th135pakB->Find(path);
if (info == nullptr && infoB == nullptr) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
OggVorbis_File ovf;
Th135Reader * const readerPtr = new Th135Reader(*(infoB ? th135pakB : th135pak), *(infoB ? infoB : info));
if (!oggOpen(ovf, readerPtr)) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const unsigned int handle = newVirtualFileHandle();
getVirtualFileHandle(handle) = std::make_unique<VirtualWavFile>(ovf);
return handle;
}
boost::shared_ptr<TouhouSE::TH145Pak::Th145PakExtracter> th145pak = nullptr;
boost::shared_ptr<TouhouSE::TH145Pak::Th145PakExtracter> th145pakB = nullptr;
unsigned int hookCreateFileWTh145Ogg(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition) {
if (!th145pak || !boost::filesystem::is_regular_file(getRelativeRootPath(lpFileName) / L"th145.pak")) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const boost::filesystem::path path = getRelativePath(lpFileName);
const auto * const info = th145pak->Find(path);
const auto * const infoB = (!th145pakB ? nullptr : th145pakB->Find(path));
if (info == nullptr && infoB == nullptr) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
OggVorbis_File ovf;
Th145Reader * const readerPtr = new Th145Reader(*(infoB ? th145pakB : th145pak), *(infoB ? infoB : info));
if (!oggOpen(ovf, readerPtr)) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const unsigned int handle = newVirtualFileHandle();
getVirtualFileHandle(handle) = std::make_unique<VirtualWavFile>(ovf);
return handle;
}
boost::shared_ptr<TouhouSE::TH145Pak::Th145PakExtracter> th155pak = nullptr;
boost::shared_ptr<TouhouSE::TH145Pak::Th145PakExtracter> th155pakB = nullptr;
unsigned int hookCreateFileWTh155Ogg(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition) {
if (!th155pak || !boost::filesystem::is_regular_file(getRelativeRootPath(lpFileName) / L"th155.pak")) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const boost::filesystem::path path = getRelativePath(lpFileName);
const auto * const info = th155pak->Find(path);
const auto * const infoB = (!th155pakB ? nullptr : th155pakB->Find(path));
if (info == nullptr && infoB == nullptr) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
OggVorbis_File ovf;
Th145Reader * const readerPtr = new Th145Reader(*(infoB ? th155pakB : th155pak), *(infoB ? infoB : info));
if (!oggOpen(ovf, readerPtr)) {
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
const unsigned int handle = newVirtualFileHandle();
getVirtualFileHandle(handle) = std::make_unique<VirtualWavFile>(ovf);
return handle;
}
unsigned int hookCreateFileW(const wchar_t * const lpFileName, const DWORD dwDesiredAccess, const DWORD dwCreationDisposition) {
const unsigned int th135ListResult = hookCreateFileWTh135List(lpFileName, dwDesiredAccess, dwCreationDisposition);
if (reinterpret_cast<HANDLE>(th135ListResult) != INVALID_HANDLE_VALUE) {
return th135ListResult;
}
const unsigned int th145ListResult = hookCreateFileWTh145List(lpFileName, dwDesiredAccess, dwCreationDisposition);
if (reinterpret_cast<HANDLE>(th145ListResult) != INVALID_HANDLE_VALUE) {
return th145ListResult;
}
const unsigned int th155ListResult = hookCreateFileWTh155List(lpFileName, dwDesiredAccess, dwCreationDisposition);
if (reinterpret_cast<HANDLE>(th155ListResult) != INVALID_HANDLE_VALUE) {
return th155ListResult;
}
const unsigned int nativeOggResult = hookCreateFileWNativeOgg(lpFileName, dwDesiredAccess, dwCreationDisposition);
if (reinterpret_cast<HANDLE>(nativeOggResult) != INVALID_HANDLE_VALUE) {
return nativeOggResult;
}
const unsigned int th135OggResult = hookCreateFileWTh135Ogg(lpFileName, dwDesiredAccess, dwCreationDisposition);
if (reinterpret_cast<HANDLE>(th135OggResult) != INVALID_HANDLE_VALUE) {
return th135OggResult;
}
const unsigned int th145OggResult = hookCreateFileWTh145Ogg(lpFileName, dwDesiredAccess, dwCreationDisposition);
if (reinterpret_cast<HANDLE>(th145OggResult) != INVALID_HANDLE_VALUE) {
return th145OggResult;
}
const unsigned int th155OggResult = hookCreateFileWTh155Ogg(lpFileName, dwDesiredAccess, dwCreationDisposition);
if (reinterpret_cast<HANDLE>(th155OggResult) != INVALID_HANDLE_VALUE) {
return th155OggResult;
}
return reinterpret_cast<unsigned int>(INVALID_HANDLE_VALUE);
}
bool hookOpenArchiveTh135() {
const boost::filesystem::path th135PakPath = L"th135.pak";
const boost::filesystem::path th135PakBPath = L"th135b.pak";
if (!boost::filesystem::is_regular_file(th135PakPath) || !boost::filesystem::is_regular_file(th135PakBPath)) {
return false;
}
if (th135pak && th135pakB) {
return true;
}
std::unique_ptr<boost::filesystem::ifstream> ifs = std::make_unique<boost::filesystem::ifstream>(th135PakPath, std::ios::binary);
th135pak = TouhouSE::TH135::Th135Extracter::Open(std::move(ifs), boost::filesystem::file_size(th135PakPath));
if (!th135pak) {
return false;
}
ifs = std::make_unique<boost::filesystem::ifstream>(th135PakBPath, std::ios::binary);
th135pakB = TouhouSE::TH135::Th135Extracter::Open(std::move(ifs), boost::filesystem::file_size(th135PakBPath));
return !!th135pakB;
}
bool hookOpenArchiveTh145() {
const boost::filesystem::path th145PakPath = L"th145.pak";
if (!boost::filesystem::is_regular_file(th145PakPath)) {
return false;
}
if (th145pak) {
return true;
}
std::unique_ptr<boost::filesystem::ifstream> ifs = std::make_unique<boost::filesystem::ifstream>(th145PakPath, std::ios::binary);
th145pak = TouhouSE::TH145::openTh145Extracter(std::move(ifs), boost::filesystem::file_size(th145PakPath));
if (!th145pak) {
return false;
}
const boost::filesystem::path th145PakBPath = L"th145b.pak";
if (!boost::filesystem::is_regular_file(th145PakBPath)) {
return true;
}
ifs = std::make_unique<boost::filesystem::ifstream>(th145PakBPath, std::ios::binary);
th145pakB = TouhouSE::TH145::openTh145Extracter(std::move(ifs), boost::filesystem::file_size(th145PakBPath));
return true;
}
bool hookOpenArchiveTh155() {
const boost::filesystem::path th155PakPath = L"th155.pak";
if (!boost::filesystem::is_regular_file(th155PakPath)) {
return false;
}
if (th155pak) {
return true;
}
std::unique_ptr<boost::filesystem::ifstream> ifs = std::make_unique<boost::filesystem::ifstream>(th155PakPath, std::ios::binary);
th155pak = TouhouSE::TH155::openTh155Extracter(std::move(ifs), boost::filesystem::file_size(th155PakPath));
if (!th155pak) {
return false;
}
const boost::filesystem::path th155PakBPath = L"th155b.pak";
if (!boost::filesystem::is_regular_file(th155PakBPath)) {
return true;
}
ifs = std::make_unique<boost::filesystem::ifstream>(th155PakBPath, std::ios::binary);
th155pakB = TouhouSE::TH155::openTh155Extracter(std::move(ifs), boost::filesystem::file_size(th155PakBPath));
return true;
}
template<typename LIST, typename EXTRACTER>
bool isTargetFile(const wchar_t * const pszPath, const LIST &list, bool (*openProc)(), EXTRACTER &pakA, EXTRACTER &pakB) {
const boost::filesystem::path path = pszPath;
const auto endIt = &list[_countof(list)];
const auto it = std::find_if(&list[0], endIt, [path](const auto &item) {
return path == item.path;
});
if (it != endIt) {
if (openProc()) {
if (pakA->Find(path) != nullptr || (pakB && pakB->Find(path) != nullptr)) {
return true;
}
}
}
return false;
}
bool isTh135File(const wchar_t * const pszPath) {
return isTargetFile(pszPath, th135MusicList, hookOpenArchiveTh135, th135pak, th135pakB);
}
bool isTh145File(const wchar_t * const pszPath) {
return isTargetFile(pszPath, th145MusicList, hookOpenArchiveTh145, th145pak, th145pakB);
}
bool isTh155File(const wchar_t * const pszPath) {
return isTargetFile(pszPath, th155MusicList, hookOpenArchiveTh155, th155pak, th155pakB);
}
bool hookPathFileExistsW(const wchar_t * const pszPath) {
return isTh135File(pszPath) || isTh145File(pszPath) || isTh155File(pszPath);
}
// 独自機能ここまで
bool ChangeIAT(HMODULE module) {
static const struct {
const std::string dllName;
const std::string procName;
const unsigned int procAddress;
} targetList[] = {
{ "KERNEL32.dll", "FindFirstFileW", reinterpret_cast<unsigned int>(d_FindFirstFileW) },
{ "KERNEL32.dll", "FindNextFileW", reinterpret_cast<unsigned int>(d_FindNextFileW) },
{ "KERNEL32.dll", "FindClose", reinterpret_cast<unsigned int>(d_FindClose) },
{ "KERNEL32.dll", "CreateFileW", reinterpret_cast<unsigned int>(d_CreateFileW) },
{ "KERNEL32.dll", "CloseHandle", reinterpret_cast<unsigned int>(d_CloseHandle) },
{ "KERNEL32.dll", "ReadFile", reinterpret_cast<unsigned int>(d_ReadFile) },
{ "KERNEL32.dll", "SetFilePointer", reinterpret_cast<unsigned int>(d_SetFilePointer) },
{ "KERNEL32.dll", "WriteFile", reinterpret_cast<unsigned int>(d_WriteFile) },
{ "KERNEL32.dll", "GetFileType", reinterpret_cast<unsigned int>(d_GetFileType) },
{ "SHLWAPI.dll", "PathFileExistsW", reinterpret_cast<unsigned int>(d_PathFileExistsW) },
};
unsigned char * const baseAddr = reinterpret_cast<unsigned char *>(module == NULL ? ::GetModuleHandleW(NULL) : module);
const IMAGE_DOS_HEADER &mz = *reinterpret_cast<const IMAGE_DOS_HEADER *>(baseAddr);
const IMAGE_NT_HEADERS32 &pe = *reinterpret_cast<const IMAGE_NT_HEADERS32 *>(baseAddr + mz.e_lfanew);
const IMAGE_IMPORT_DESCRIPTOR *desc = reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR *>(baseAddr + pe.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
for (; desc->OriginalFirstThunk != 0; desc = ++desc) {
const char * const dllName = reinterpret_cast<const char *>(baseAddr + desc->Name);
const unsigned int *in = reinterpret_cast<unsigned int *>(baseAddr + desc->OriginalFirstThunk);
unsigned int *out = reinterpret_cast<unsigned int *>(baseAddr + desc->FirstThunk);
for (; *in != 0; ++in, ++out) {
if ((*in & 0x80000000) != 0) {
continue;
}
const char * const procName = reinterpret_cast<const char *>(baseAddr + *in + 2);
for (const auto &target : targetList) {
if (::_stricmp(target.dllName.c_str(), dllName) != 0 || target.procName != procName) {
continue;
}
DWORD oldProtect;
::VirtualProtect(out, 4, PAGE_READWRITE, &oldProtect);
*out = target.procAddress;
::VirtualProtect(out, 4, oldProtect, &oldProtect);
}
}
}
return true;
}
} // anonymous
void main() {
// debug用
//org::click3::DllHackLib::SetupConsole();
const bool changeResult = ChangeIAT(NULL);
}