他人の空似自作物置場

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;
}