touhouSE_th145BGMOnly.zip/touhouSE_src/main.cpp
#include "stdafx.h"
#pragma comment(lib,"Comdlg32.lib")
using namespace Utility;
DatExtractor *DatExtractor::instance = NULL;
FileConverter *FileConverter::instance = NULL;
char dat_name[256];
namespace {
struct SumFileCompSize {
unsigned int operator()(unsigned int value, boost::shared_ptr<const LIST> file_data) {
return value + file_data->comp_size;
}
};
} // anonymous
DAT_VER GetList(std::vector<boost::shared_ptr<LIST> > &list, boost::shared_ptr<FILE> fp) {
static bool (* const func_list[])(std::vector<boost::shared_ptr<LIST> > &, boost::shared_ptr<FILE>) = {TH105::GetList, TH075::GetList, TH11::GetList, TH12::GetList, TH13::GetList, THMJ::GetList, TNB::GetList, NPA::GetList};
for(unsigned int i = 0; i < DAT_VAR_MAX; i++) {
const bool result = func_list[DAT_VAR_MAX - 1 - i](list, fp);
if(result) {
DAT_VER dat_ver = static_cast<DAT_VER>(DAT_VAR_MAX - 1 - i);
printf("%d個のファイルを検出しました。\n", list.size());
return dat_ver;
}
}
return DAT_VER_UNKNOWN;
}
bool PutFile(DAT_VER dat_ver, boost::shared_ptr<const LIST> file_data, const std::vector<unsigned char> &data, const std::vector<boost::shared_ptr<LIST> > &list, boost::shared_ptr<FILE> fp) {
static bool (* const func[])(boost::shared_ptr<const LIST>, const std::vector<unsigned char> &, const std::vector<boost::shared_ptr<LIST> > &, boost::shared_ptr<FILE>) = {TH105::PutFile, TH075::PutFile, TH11::PutFile, TH12::PutFile, TH13::PutFile, THMJ::PutFile, TNB::PutFile, NPA::PutFile};
return func[dat_ver](file_data, data, list, fp);
}
bool Extract(std::vector<unsigned char> &data, DAT_VER dat_ver, boost::shared_ptr<const LIST> file_data, boost::shared_ptr<FILE> fp) {
static bool (* const func[])(std::vector<unsigned char> &, boost::shared_ptr<const LIST>, boost::shared_ptr<FILE>) = {TH105::Extract, TH075::Extract, TH11::Extract, TH12::Extract, TH13::Extract, THMJ::Extract, TNB::Extract, NPA::Extract};
return func[dat_ver](data, file_data, fp);
}
const char *Open(const char *s){
static char fn[256];
if(s == NULL || strlen(s)==0){
std::vector<char> dir;
GetAppDir(dir);
dir.resize(dir.size() + 1);
::PathAddBackslashA(&dir.front());
OPENFILENAMEA ofn;
memset(&ofn,0,sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.lpstrFilter = "すべての対応形式(*.dat;*.bin;*.npa)\0*.dat;*.bin;*.npa\0All files(*.*)\0*.*\0\0";
ofn.lpstrFile = fn;
ofn.nMaxFile = sizeof(fn);
ofn.lpstrInitialDir = &dir.front();
ofn.Flags = OFN_FILEMUSTEXIST;
ofn.lpstrTitle = "ファイルを開く";
ofn.lpstrDefExt = "dat";
if(!GetOpenFileNameA(&ofn)){
return NULL;
}
} else {
STRCPY(fn, s);
}
SetAppDir();
return fn;
}
bool NormalPutFile(boost::shared_ptr<const LIST> file_data, const std::vector<unsigned char> &data) {
if(!file_data) {
return false;
}
char put_filename[256] = "";
const char * const filename = ::PathFindFileNameA(file_data->fn);
if(filename == file_data->fn) {
STRCPY(put_filename, "data/");
}
STRCAT(put_filename, file_data->fn);
boost::shared_ptr<FILE> fp = MyFOpen(put_filename, "wb");
if(!fp) {
return false;
}
if(!data.empty()) {
if(data.size() != ::fwrite(&data.front(), 1, data.size(), fp.get())) {
return false;
}
}
return true;
}
void DebugConvert(const boost::filesystem::path &path, std::istream &is, const unsigned long long int fileSize) {
if (fileSize == 0) {
return;
}
std::vector<unsigned char> data(static_cast<unsigned int>(fileSize));
is.read(reinterpret_cast<char *>(&data.front()), fileSize);
boost::filesystem::path outPath;
std::vector<unsigned char> convertData;
const bool convertResult = FileConverter::GetInstance().Convert(path, data, outPath, convertData, *reinterpret_cast<ExtractorBase *>(NULL));
if (!convertResult) {
return;
}
boost::filesystem::ofstream ofs(outPath, std::ios::binary);
if (!convertData.empty()) {
ofs.write(reinterpret_cast<const char *>(&convertData.front()), convertData.size());
}
ofs.close();
}
bool DatExtractor::ExtractAll(const boost::filesystem::path &path) {
if (!boost::filesystem::is_regular_file(path)) {
std::wcout << boost::wformat(L"\n%1%がファイルではありません。\nファイル以外の展開はサポートされません。\n") % path.wstring();
return false;
}
const unsigned long long int fileSize = boost::filesystem::file_size(path);
boost::filesystem::ifstream ifs(path, boost::filesystem::ifstream::binary);
if (!ifs.is_open()) {
std::wcout << boost::wformat(L"\n%1%を開けませんでした。\n%1%の関連アプリを終了して実行しなおしてください。\n") % path.wstring();
return false;
}
boost::shared_ptr<ExtractorBase> extractor;
BOOST_FOREACH(boost::function<boost::shared_ptr<ExtractorBase> (std::istream &, const unsigned long long int)> func, this->openFuncList) {
extractor = func(ifs, fileSize);
if (extractor) {
break;
}
ifs.seekg(0, boost::filesystem::ifstream::beg);
}
if (!extractor) {
//DebugConvert(path, ifs, fileSize);
std::wcout << boost::wformat(L"\n%1%は非対応のファイルフォーマットです。\n") % path.wstring();
return false;
}
std::wcout << boost::wformat(L"\nフォーマット:%1%\n展開を始めます。\n\n") % extractor->GetName();
time_t start;
time(&start);
bool result = true;
const unsigned int totalSize = extractor->GetSize();
for (unsigned int i = 0; i < totalSize; i++) {
const boost::filesystem::path fileName = extractor->GetFileName(i);
std::vector<unsigned char> data;
const bool extractResult = extractor->Extract(i, data);
if(!extractResult) {
std::wcout << boost::wformat(L"\nファイル%1%の展開に失敗しました。\n%1%は無視されます。\n") % fileName.wstring();
result = false;
} else {
result &= ExtractImpl(fileName, data, *extractor);
}
time_t elapsed;
time(&elapsed);
elapsed = elapsed - start;
unsigned int remainder = static_cast<unsigned int>(static_cast<double>(totalSize - i) * elapsed/i);//time_tが4バイトの場合オーバーフローするのでdoubleにキャスト
std::wcout << boost::wformat(L"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%6u/%6u %02u:%02u(残り%02u:%02u)") %
i % totalSize % static_cast<unsigned int>(elapsed/60) % static_cast<unsigned int>(elapsed%60) % static_cast<unsigned int>(remainder/60) % static_cast<int>(remainder%60);
}
std::cout << std::endl;
return true;
}
bool DatExtractor::ExtractImpl(const boost::filesystem::path &fileName, const std::vector<unsigned char> &data, ExtractorBase &ownerExtractor) {
if (data.size() > 0) {
const boost::iostreams::array_source source(reinterpret_cast<const char *>(&data.front()), reinterpret_cast<const char *>(&data.back() + 1));
boost::iostreams::stream<boost::iostreams::array_source> in(source);
boost::shared_ptr<ExtractorBase> extractor;
BOOST_FOREACH(boost::function<boost::shared_ptr<ExtractorBase> (std::istream &, const unsigned long long int)> func, this->openFuncList) {
extractor = func(in, data.size());
if (extractor) {
break;
}
in.seekg(0, boost::filesystem::ifstream::beg);
}
if (extractor) {
const boost::filesystem::path outDir = boost::filesystem::path(fileName).remove_filename();
const unsigned int totalSize = extractor->GetSize();
bool result = true;
for (unsigned int i = 0; i < totalSize; i++) {
const boost::filesystem::path fileName = outDir / extractor->GetFileName(i);
std::vector<unsigned char> data;
const bool extractResult = extractor->Extract(i, data);
if(!extractResult) {
std::wcout << boost::wformat(L"\nファイル%1%の展開に失敗しました。\n%1%は無視されます。\n") % fileName.wstring();
result = false;
continue;
}
result &= ExtractImpl(fileName, data, *extractor);
}
return result;
}
}
boost::filesystem::path outPath;
std::vector<unsigned char> convertData;
const bool convertResult = FileConverter::GetInstance().Convert(fileName, data, outPath, convertData, ownerExtractor);
const std::vector<unsigned char> &outData = convertResult ? convertData : data;
if (!convertResult) {
outPath = fileName;
}
const boost::filesystem::path outDir = boost::filesystem::path(outPath).remove_filename();
try {
boost::filesystem::create_directories(boost::filesystem::absolute(outDir));
} catch(const boost::filesystem::filesystem_error &error) {
std::cout << error.what();
return false;
}
if (boost::filesystem::exists(outPath)) {
const boost::filesystem::path ext = outPath.extension();
const boost::filesystem::path name = outPath.replace_extension().filename();
const boost::filesystem::path dir = outPath.remove_filename();
for (unsigned int i = 1; ; i++) {
const std::wstring filename = (boost::wformat(L"%s(%d)") % name.wstring() % i).str();
const boost::filesystem::path temp = boost::filesystem::path(dir / filename).replace_extension(ext);
if (!boost::filesystem::exists(temp)) {
outPath = temp;
break;
}
}
}
boost::filesystem::ofstream ofs(outPath, boost::filesystem::ofstream::binary);
if (!ofs.is_open()) {
std::wcout << boost::wformat(L"\nファイル%1%の出力に失敗しました。\n%1%は無視されます。\n") % fileName.wstring();
return false;
}
if (outData.size() > 0) {
ofs.write(reinterpret_cast<const char *>(&outData.front()), outData.size());
if (!ofs.good()) {
std::wcout << boost::wformat(L"\nファイル%1%の出力に失敗しました。\n%1%は無視されます。\n") % fileName.wstring();
return false;
}
}
ofs.close();
return true;
}
bool DatExtract(const std::vector<std::string> &filename_list) {
bool result = true;
BOOST_FOREACH(std::string filename, filename_list) {
printf("%sを展開します。\n", filename.c_str());
STRCPY(dat_name, filename.c_str());
const boost::shared_ptr<FILE> fp = MyFOpen(filename.c_str(), "rb");
std::vector<boost::shared_ptr<LIST> > file_list;
const DAT_VER dat_ver = GetList(file_list, fp);
if(dat_ver == DAT_VER_UNKNOWN) {
if (!DatExtractor::GetInstance().ExtractAll(filename)) {
result = false;
}
continue;
} else {
const char * const dat_type[] = {"緋想天", "萃夢想", "地霊殿", "星蓮船", "神霊廟", "幻想麻雀", "テンドーブレード", "NitroPlusArchive"};
if(!file_list.empty()) {
printf("\nフォーマット:%s\nファイル展開を始めます。\n\n", dat_type[dat_ver]);
}
time_t start;
time(&start);
const unsigned int total_size = std::accumulate(file_list.begin(), file_list.end(), static_cast<unsigned int>(0), SumFileCompSize());
unsigned int current_size = 0;
unsigned int count = 0;
BOOST_FOREACH(boost::shared_ptr<const LIST> file_data, file_list) {
current_size += file_data->comp_size;
count++;
std::vector<unsigned char> data;
const bool extract_result = Extract(data, dat_ver, file_data, fp);
if(!extract_result) {
printf("\nファイル%sの展開に失敗しました。\n%sは無視されます。\n", file_data->fn, file_data->fn);
result = false;
continue;
}
const bool put_result = PutFile(dat_ver, file_data, data, file_list, fp);
if(!put_result) {
result = false;
const bool normal_put_result = NormalPutFile(file_data, data);
if(normal_put_result) {
printf("\nファイル%sの出力に失敗しました。\n%sは無変換で出力されます。\n", file_data->fn, file_data->fn);
} else {
printf("\nファイル%sの出力に失敗しました。\n%sは無視されます。\n", file_data->fn, file_data->fn);
}
}
time_t elapsed;
time(&elapsed);
elapsed = elapsed - start;
unsigned int remainder = static_cast<unsigned int>(static_cast<double>(total_size - current_size) * elapsed/current_size);//time_tが4バイトの場合オーバーフローするのでdoubleにキャスト
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%6u/%6u %02u:%02u(残り%02u:%02u)",
count, file_list.size(), static_cast<unsigned int>(elapsed/60), static_cast<unsigned int>(elapsed%60), static_cast<unsigned int>(remainder/60), static_cast<int>(remainder%60));
}
}
printf("\n展開終了しました\n");
}
return result;
}
bool ParseArgs(std::vector<std::string> &filename_list, bool &is_enter_sleep, unsigned int argc, const char * const argv[]) {
is_enter_sleep = true;
if(argc == 0 || argv == NULL) {
return false;
}
for(unsigned int i = 0; i < argc; i++) {
if(argv[i] == NULL) {
return false;
}
}
if(argc == 1) {
const char * const open_filename = Open(NULL);
if(open_filename == NULL) {
return false;
}
filename_list.push_back(open_filename);
return true;
}
bool result = true;
for(unsigned int i = 1; i < argc; i++) {
if(argv[i][0] == '-' || argv[i][0] == '/') {
switch(argv[i][1]) {
case 'c':
is_enter_sleep = false;
break;
default:
result = false;
printf("無効なオプション(%s)\n", &argv[i][1]);
break;
}
continue;
}
filename_list.push_back(argv[i]);
}
return result;
}
int main(unsigned int argc, const char * const argv[]) {
std::locale loc = std::locale("japanese").combine<std::numpunct<char> >(std::locale::classic()).combine<std::numpunct<wchar_t> >(std::locale::classic());
std::locale::global(loc);
std::wcout.imbue(loc);
std::cout.imbue(loc);
SetAppDir();
std::vector<std::string> filename_list;
bool is_enter_sleep;
if(!ParseArgs(filename_list, is_enter_sleep, argc, argv)) {
return 1;
}
const bool result = DatExtract(filename_list);
return result ? 0 : 1;
}