diff --git a/cmake/project_files.cmake b/cmake/project_files.cmake index 998d497..71db071 100644 --- a/cmake/project_files.cmake +++ b/cmake/project_files.cmake @@ -8,28 +8,30 @@ file(GLOB_RECURSE LIBCGSS_INS_SOURCE_FILES "src/lib/ichinose/*.h" "src/lib/ichin set(LIBCGSS_SOURCE_FILES ${LIBCGSS_API_FILES} ${LIBCGSS_COMMON_HEADER_FILES} ${LIBCGSS_COMMON_SOURCE_FILES} ${LIBCGSS_KS_SOURCE_FILES} ${LIBCGSS_TM_SOURCE_FILES} ${LIBCGSS_INS_SOURCE_FILES}) -set(CGSS_APP_SOURCE_FILES +set(CGSS_API_HEADER_FILES src/apps/cgssh.h) -set(APP_SOURCE_FILES +set(APP_COMMON_SOURCE_FILES src/apps/common/common.h src/apps/common/atoh.hpp src/apps/common/utils.h src/apps/common/utils.cpp) +set(APP_ACB_EXTRACT_COMMON_FILES + src/apps/common/acbextract.h src/apps/common/acbextract.cpp) set(HCACC_SOURCE_FILES - src/apps/hcacc/hcacc.cpp ${CGSS_APP_SOURCE_FILES} ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) + src/apps/hcacc/hcacc.cpp ${CGSS_API_HEADER_FILES} ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) set(HCAENC_SOURCE_FILES - src/apps/hcaenc/hcaenc.cpp ${CGSS_APP_SOURCE_FILES} ${APP_SOURCE_FILES} ${LIBCGSS_TM_API_FILES}) + src/apps/hcaenc/hcaenc.cpp ${CGSS_API_HEADER_FILES} ${APP_SOURCE_FILES} ${LIBCGSS_TM_API_FILES}) set(HCA2WAV_SOURCE_FILES - src/apps/hca2wav/hca2wav.cpp ${CGSS_APP_SOURCE_FILES} ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) + src/apps/hca2wav/hca2wav.cpp ${CGSS_API_HEADER_FILES} ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) set(HCAINFO_SOURCE_FILES - src/apps/hcainfo/hcainfo.cpp ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) + src/apps/hcainfo/hcainfo.cpp ${APP_COMMON_SOURCE_FILES} ${LIBCGSS_API_FILES}) set(UTFTABLE_SOURCE_FILES - src/apps/utftable/utftable.c ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) + src/apps/utftable/utftable.c ${APP_COMMON_SOURCE_FILES} ${LIBCGSS_API_FILES}) set(ACBUNPACK_SOURCE_FILES - src/apps/acbunpack/acbunpack.cpp ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) + src/apps/acbunpack/acbunpack.cpp ${APP_COMMON_SOURCE_FILES} ${LIBCGSS_API_FILES} ${APP_ACB_EXTRACT_COMMON_FILES}) set(ACB2WAVS_SOURCE_FILES - src/apps/acb2wavs/acb2wavs.cpp ${CGSS_APP_SOURCE_FILES} ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) + src/apps/acb2wavs/acb2wavs.cpp ${CGSS_API_HEADER_FILES} ${APP_COMMON_SOURCE_FILES} ${LIBCGSS_API_FILES} ${APP_ACB_EXTRACT_COMMON_FILES}) set(ACB2HCAS_SOURCE_FILES - src/apps/acb2hcas/acb2hcas.cpp ${APP_SOURCE_FILES} ${LIBCGSS_API_FILES}) + src/apps/acb2hcas/acb2hcas.cpp ${APP_COMMON_SOURCE_FILES} ${LIBCGSS_API_FILES} ${APP_ACB_EXTRACT_COMMON_FILES}) set(LIBCGSS_DEF_FILE src/cgss.def) set(LIBCGSS_SOURCE_FILES ${LIBCGSS_SOURCE_FILES} ${LIBCGSS_DEF_FILE}) diff --git a/src/apps/acb2hcas/acb2hcas.cpp b/src/apps/acb2hcas/acb2hcas.cpp index ccec5a1..0466e35 100644 --- a/src/apps/acb2hcas/acb2hcas.cpp +++ b/src/apps/acb2hcas/acb2hcas.cpp @@ -6,6 +6,7 @@ #include "../../lib/cgss_api.h" #include "../common/common.h" +#include "../common/acbextract.h" using namespace std; using namespace cgss; @@ -27,8 +28,7 @@ static int ParseArgs(int argc, const char *argv[], const char **input, Acb2HcasO static int DoWork(const string &inputFile, const Acb2HcasOptions &options); -static int -ProcessAllBinaries(CAcbFile *acb, uint32_t formatVersion, const Acb2HcasOptions &options, const string &extractDir, CAfs2Archive *archive, IStream *dataStream, bool_t isInternal); +static int ProcessHca(AcbWalkCallbackParams *params); static void WriteHcaKeyFile(const string &fileName, uint64_t key, uint16_t modifier = 0); @@ -100,139 +100,45 @@ static int ParseArgs(int argc, const char *argv[], const char **input, Acb2HcasO } static int DoWork(const string &inputFile, const Acb2HcasOptions &options) { - const auto baseExtractDirPath = CPath::Combine(CPath::GetDirectoryName(inputFile), "_acb_" + CPath::GetFileName(inputFile)); + AcbWalkOptions o; - CFileStream fileStream(inputFile.c_str(), FileMode::OpenExisting, FileAccess::Read); - CAcbFile acb(&fileStream, inputFile.c_str()); + o.callback = ProcessHca; + o.decoderConfig.cipherConfig.key = options.key; + o.useCueName = options.useCueName; - acb.Initialize(); - - CAfs2Archive *archive = nullptr; - uint32_t formatVersion = acb.GetFormatVersion(); - int r; - - try { - archive = acb.GetInternalAwb(); - } catch (CException &ex) { - fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - archive = nullptr; - } - - if (archive) { - const auto extractDir = CPath::Combine(baseExtractDirPath, "internal"); - - try { - r = ProcessAllBinaries(&acb, formatVersion, options, extractDir, archive, acb.GetStream(), TRUE); - } catch (CException &ex) { - fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - r = -1; - } - - delete archive; - - if (r != 0) { - return r; - } - } - - try { - archive = acb.GetExternalAwb(); - } catch (CException &ex) { - fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - archive = nullptr; - } - - if (archive) { - const auto extractDir = CPath::Combine(baseExtractDirPath, "external"); - - try { - CFileStream fs(archive->GetFileName(), FileMode::OpenExisting, FileAccess::Read); - - r = ProcessAllBinaries(&acb, formatVersion, options, extractDir, archive, &fs, FALSE); - } catch (CException &ex) { - fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - r = -1; - } - - delete archive; - - if (r != 0) { - return r; - } - } - - return 0; + return AcbWalk(inputFile, &o); } -static int -ProcessAllBinaries(CAcbFile *acb, uint32_t formatVersion, const Acb2HcasOptions &options, const string &extractDir, CAfs2Archive *archive, IStream *dataStream, bool_t isInternal) { - if (!CFileSystem::DirectoryExists(extractDir)) { - if (!CFileSystem::MkDir(extractDir)) { - fprintf(stderr, "Failed to create directory %s.\n", extractDir.c_str()); - return -1; - } - } +static int ProcessHca(AcbWalkCallbackParams *params) { + const auto afsSourceDesc = params->isInternal ? "internal" : "external"; + const auto isHca = CHcaFormatReader::IsPossibleHcaStream(params->entryDataStream); - const auto afsSource = isInternal ? "internal" : "external"; + fprintf(stdout, "Processing %s AFS: #%" PRIu32 " (offset=%" PRIu32 ", size=%" PRIu32 ")", + afsSourceDesc, (uint32_t)params->cueInfo.id, (uint32_t)params->cueInfo.offset, (uint32_t)params->cueInfo.size); - uint16_t keyModifier; + if (isHca) { + std::string extractFilePath = common_utils::ReplaceAnyExtension(params->extractPathHint, ".hca"); + std::string hcaKeyFilePath = common_utils::ReplaceAnyExtension(params->extractPathHint, ".hcakey"); - if (formatVersion >= CAcbFile::KEY_MODIFIER_ENABLED_VERSION) { - keyModifier = archive->GetHcaKeyModifier(); - } else { - keyModifier = 0; - } - - const auto &fileList = archive->GetFiles(); - - for (auto &entry : fileList) { - auto &record = entry.second; + fprintf(stdout, " to %s...\n", extractFilePath.c_str()); - auto fileData = CAcbHelper::ExtractToNewStream(dataStream, record.fileOffsetAligned, (uint32_t)record.fileSize); - - const auto isHca = CHcaFormatReader::IsPossibleHcaStream(fileData); + try { + CFileStream hcaFile(extractFilePath.c_str(), FileMode::Create, FileAccess::Write); - fprintf(stdout, "Processing %s AFS: #%" PRIu32 " (offset=%" PRIu32 ", size=%" PRIu32 ")", - afsSource, (uint32_t)record.cueId, (uint32_t)record.fileOffsetAligned, (uint32_t)record.fileSize); + common_utils::CopyStream(params->entryDataStream, &hcaFile); - if (isHca) { - std::string extractFileName; - std::string hcaKeyFileName; + WriteHcaKeyFile(hcaKeyFilePath, params->walkOptions->decoderConfig.cipherConfig.key, params->walkOptions->decoderConfig.cipherConfig.keyModifier); - if (options.useCueName) { - extractFileName = acb->GetCueNameFromCueId(record.cueId); - } else { - extractFileName = CAcbFile::GetSymbolicFileNameFromCueId(record.cueId); + fprintf(stdout, "exported\n"); + } catch (CException &ex) { + if (CFileSystem::FileExists(extractFilePath)) { + CFileSystem::RmFile(extractFilePath); } - extractFileName = common_utils::ReplaceAnyExtension(extractFileName, ".hca"); - hcaKeyFileName = common_utils::ReplaceAnyExtension(extractFileName, ".hcakey"); - - auto extractFilePath = CPath::Combine(extractDir, extractFileName); - auto hcaKeyFilePath = CPath::Combine(extractDir, hcaKeyFileName); - - fprintf(stdout, " to %s...\n", extractFilePath.c_str()); - - try { - CFileStream hcaFile(extractFilePath.c_str(), FileMode::Create, FileAccess::Write); - - common_utils::CopyStream(fileData, &hcaFile); - - WriteHcaKeyFile(hcaKeyFilePath, options.key, keyModifier); - - fprintf(stdout, "exported\n"); - } catch (CException &ex) { - if (CFileSystem::FileExists(extractFilePath)) { - CFileSystem::RmFile(extractFilePath); - } - - fprintf(stdout, "errored: %s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - } - } else { - fprintf(stdout, "... skipped (not HCA)\n"); + fprintf(stdout, "errored: %s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); } - - delete fileData; + } else { + fprintf(stdout, "... skipped (not HCA)\n"); } return 0; diff --git a/src/apps/acb2wavs/acb2wavs.cpp b/src/apps/acb2wavs/acb2wavs.cpp index 22850ca..5a988e7 100644 --- a/src/apps/acb2wavs/acb2wavs.cpp +++ b/src/apps/acb2wavs/acb2wavs.cpp @@ -6,6 +6,7 @@ #include "../cgssh.h" #include "../../lib/cgss_api.h" #include "../common/common.h" +#include "../common/acbextract.h" using namespace cgss; using namespace std; @@ -21,8 +22,7 @@ static int ParseArgs(int argc, const char *argv[], const char **input, Acb2WavsO static int DoWork(const string &inputFile, const Acb2WavsOptions &options); -static int -ProcessAllBinaries(CAcbFile *acb, uint32_t formatVersion, const Acb2WavsOptions &options, const string &extractDir, CAfs2Archive *archive, IStream *dataStream, bool_t isInternal); +static int ProcessHca(AcbWalkCallbackParams *params); static int DecodeHca(IStream *hcaDataStream, IStream *waveStream, const HCA_DECODER_CONFIG &dc); @@ -91,137 +91,49 @@ static int ParseArgs(int argc, const char *argv[], const char **input, Acb2WavsO } static int DoWork(const string &inputFile, const Acb2WavsOptions &options) { - const auto baseExtractDirPath = CPath::Combine(CPath::GetDirectoryName(inputFile), "_acb_" + CPath::GetFileName(inputFile)); + AcbWalkOptions o; - CFileStream fileStream(inputFile.c_str(), FileMode::OpenExisting, FileAccess::Read); - CAcbFile acb(&fileStream, inputFile.c_str()); + o.callback = ProcessHca; + o.decoderConfig = options.decoderConfig; + o.useCueName = options.useCueName; - acb.Initialize(); - - CAfs2Archive *archive = nullptr; - uint32_t formatVersion = acb.GetFormatVersion(); - int r; - - try { - archive = acb.GetInternalAwb(); - } catch (CException &ex) { - fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - archive = nullptr; - } - - if (archive) { - const auto extractDir = CPath::Combine(baseExtractDirPath, "internal"); - - try { - r = ProcessAllBinaries(&acb, formatVersion, options, extractDir, archive, acb.GetStream(), TRUE); - } catch (CException &ex) { - fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - r = -1; - } - - delete archive; - - if (r != 0) { - return r; - } - } - - try { - archive = acb.GetExternalAwb(); - } catch (CException &ex) { - fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - archive = nullptr; - } - - if (archive) { - const auto extractDir = CPath::Combine(baseExtractDirPath, "external"); - - try { - CFileStream fs(archive->GetFileName(), FileMode::OpenExisting, FileAccess::Read); - - r = ProcessAllBinaries(&acb, formatVersion, options, extractDir, archive, &fs, FALSE); - } catch (CException &ex) { - fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); - r = -1; - } - - delete archive; - - if (r != 0) { - return r; - } - } - - return 0; + return AcbWalk(inputFile, &o); } -static int -ProcessAllBinaries(CAcbFile *acb, uint32_t formatVersion, const Acb2WavsOptions &options, const string &extractDir, CAfs2Archive *archive, IStream *dataStream, bool_t isInternal) { - if (!CFileSystem::DirectoryExists(extractDir)) { - if (!CFileSystem::MkDir(extractDir)) { - fprintf(stderr, "Failed to create directory %s.\n", extractDir.c_str()); - return -1; - } - } - - const auto afsSource = isInternal ? "internal" : "external"; - HCA_DECODER_CONFIG decoderConfig = options.decoderConfig; - - if (formatVersion >= CAcbFile::KEY_MODIFIER_ENABLED_VERSION) { - decoderConfig.cipherConfig.keyModifier = archive->GetHcaKeyModifier(); - } else { - decoderConfig.cipherConfig.keyModifier = 0; - } +static int ProcessHca(AcbWalkCallbackParams *params) { + const auto afsSource = params->isInternal ? "internal" : "external"; + const auto isHca = CHcaFormatReader::IsPossibleHcaStream(params->entryDataStream); - for (auto &entry : archive->GetFiles()) { - auto &record = entry.second; + fprintf(stdout, "Processing %s AFS: #%" PRIu32 " (offset=%" PRIu32 ", size=%" PRIu32 ")", + afsSource, (uint32_t)params->cueInfo.id, (uint32_t)params->cueInfo.offset, (uint32_t)params->cueInfo.size); - auto fileData = CAcbHelper::ExtractToNewStream(dataStream, record.fileOffsetAligned, (uint32_t)record.fileSize); + int r; - const auto isHca = CHcaFormatReader::IsPossibleHcaStream(fileData); + if (isHca) { + HCA_DECODER_CONFIG decoderConfig = params->walkOptions->decoderConfig; - fprintf(stdout, "Processing %s AFS: #%" PRIu32 " (offset=%" PRIu32 ", size=%" PRIu32 ")", - afsSource, (uint32_t)record.cueId, (uint32_t)record.fileOffsetAligned, (uint32_t)record.fileSize); + const string extractFilePath = common_utils::ReplaceAnyExtension(params->extractPathHint, ".wav"); + fprintf(stdout, "Decoding to %s...\n", extractFilePath.c_str()); - int r; + try { + CFileStream fs(extractFilePath.c_str(), FileMode::Create, FileAccess::Write); - if (isHca) { - std::string extractFileName; + r = DecodeHca(params->entryDataStream, &fs, decoderConfig); - if (options.useCueName) { - extractFileName = acb->GetCueNameFromCueId(record.cueId); + if (r == 0) { + fprintf(stdout, "decoded\n"); } else { - extractFileName = CAcbFile::GetSymbolicFileNameFromCueId(record.cueId); + fprintf(stdout, "errored\n"); } - - extractFileName = common_utils::ReplaceAnyExtension(extractFileName, ".wav"); - - auto extractFilePath = CPath::Combine(extractDir, extractFileName); - - fprintf(stdout, " to %s...\n", extractFilePath.c_str()); - - try { - CFileStream fs(extractFilePath.c_str(), FileMode::Create, FileAccess::Write); - - r = DecodeHca(fileData, &fs, decoderConfig); - - if (r == 0) { - fprintf(stdout, "decoded\n"); - } else { - fprintf(stdout, "errored\n"); - } - } catch (CException &ex) { - if (CFileSystem::FileExists(extractFilePath)) { - CFileSystem::RmFile(extractFilePath); - } - - fprintf(stdout, "errored: %s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); + } catch (CException &ex) { + if (CFileSystem::FileExists(extractFilePath)) { + CFileSystem::RmFile(extractFilePath); } - } else { - fprintf(stdout, "... skipped (not HCA)\n"); - } - delete fileData; + fprintf(stdout, "errored: %s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); + } + } else { + fprintf(stdout, "... skipped (not HCA)\n"); } return 0; diff --git a/src/apps/acbunpack/acbunpack.cpp b/src/apps/acbunpack/acbunpack.cpp index ffc2a51..79dfbac 100644 --- a/src/apps/acbunpack/acbunpack.cpp +++ b/src/apps/acbunpack/acbunpack.cpp @@ -16,6 +16,7 @@ #include "../../lib/cgss_api.h" #include "../common/common.h" +#include "../common/acbextract.h" using namespace cgss; @@ -27,6 +28,8 @@ static std::string GetDirectoryFromPath(const std::string &s); static std::string GetFileNameFromPath(const std::string &s); +static int ExtractFile(AcbWalkCallbackParams *params); + int main(int argc, const char *argv[]) { if (argc == 1) { print_help(); @@ -40,50 +43,18 @@ int main(int argc, const char *argv[]) { return -1; } - CFileStream fileStream(filePath, FileMode::OpenExisting, FileAccess::Read); - CAcbFile acb(&fileStream, filePath); - - acb.Initialize(); - - const std::string fileDir = GetDirectoryFromPath(filePath); - const std::string extractDir = fileDir + "/_acb_" + GetFileNameFromPath(filePath) + "/"; - - MakeDirectories(extractDir); - - const auto &fileNames = acb.GetFileNames(); - - uint32_t i = 0; - - for (const auto &fileName : fileNames) { - auto s = fileName; - auto isCueNonEmpty = !s.empty(); - - if (!isCueNonEmpty) { - s = CAcbFile::GetSymbolicFileNameFromCueId(i); - } - - auto extractPath = extractDir + s; + AcbWalkOptions options; + options.useCueName = true; + options.callback = ExtractFile; - IStream *stream; + const auto r = AcbWalk(filePath, &options); - if (isCueNonEmpty) { - stream = acb.OpenDataStream(s.c_str()); - } else { - stream = acb.OpenDataStream(i); - } - - if (stream) { - CFileStream fs(extractPath.c_str(), FileMode::Create, FileAccess::Write); - - common_utils::CopyStream(stream, &fs); - } else { - fprintf(stderr, "Cue #%" PRIu32 " (%s) cannot be retrieved.\n", i + 1, s.c_str()); - } - - delete stream; + return r; +} - ++i; - } +static int ExtractFile(AcbWalkCallbackParams *params) { + CFileStream fs(params->extractPathHint.c_str(), FileMode::Create, FileAccess::Write); + common_utils::CopyStream(params->entryDataStream, &fs); return 0; } diff --git a/src/apps/common/acbextract.cpp b/src/apps/common/acbextract.cpp new file mode 100644 index 0000000..507295d --- /dev/null +++ b/src/apps/common/acbextract.cpp @@ -0,0 +1,202 @@ +#include +#include +#include + +#include "acbextract.h" +#include "../common/utils.h" + +using namespace std; +using namespace cgss; + +AcbWalkCallbackParams::AcbWalkCallbackParams() + : walkOptions(nullptr), acb(nullptr), formatVersion(0), + archive(nullptr), archiveDataStream(nullptr), entryDataStream(nullptr), + isInternal(FALSE) { +} + +AcbWalkOptions::AcbWalkOptions() + : decoderConfig({}), useCueName(FALSE), callback(nullptr) { +} + +static int ProcessAllBinaries(CAcbFile &acb, uint32_t formatVersion, AcbWalkOptions *options, const string &extractDir, CAfs2Archive *archive, IStream *archiveDataStream, bool_t isInternal, unordered_set &extractedCueIds); + +int AcbWalk(const std::string &inputAcbFile, AcbWalkOptions *options) { + const auto baseExtractDirPath = CPath::Combine(CPath::GetDirectoryName(inputAcbFile), "_acb_" + CPath::GetFileName(inputAcbFile)); + + CFileStream fileStream(inputAcbFile.c_str(), FileMode::OpenExisting, FileAccess::Read); + CAcbFile acb(&fileStream, inputAcbFile.c_str()); + + acb.Initialize(); + + CAfs2Archive *archive = nullptr; + uint32_t formatVersion = acb.GetFormatVersion(); + int r; + + try { + archive = acb.GetInternalAwb(); + } catch (CException &ex) { + fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); + archive = nullptr; + } + + unordered_set extractedCueIds; + + if (archive) { + const auto extractDir = CPath::Combine(baseExtractDirPath, "internal"); + + try { + r = ProcessAllBinaries(acb, formatVersion, options, extractDir, archive, acb.GetStream(), TRUE, extractedCueIds); + } catch (CException &ex) { + fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); + r = -1; + } + + delete archive; + + if (r != 0) { + return r; + } + } + + try { + archive = acb.GetExternalAwb(); + } catch (CException &ex) { + fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); + archive = nullptr; + } + + if (archive) { + const auto extractDir = CPath::Combine(baseExtractDirPath, "external"); + + try { + CFileStream fs(archive->GetFileName(), FileMode::OpenExisting, FileAccess::Read); + + r = ProcessAllBinaries(acb, formatVersion, options, extractDir, archive, &fs, FALSE, extractedCueIds); + } catch (CException &ex) { + fprintf(stderr, "%s (%d)\n", ex.GetExceptionMessage().c_str(), ex.GetOpResult()); + r = -1; + } + + delete archive; + + if (r != 0) { + return r; + } + } + + return 0; +} + +static int ProcessAllBinaries(CAcbFile &acb, uint32_t formatVersion, AcbWalkOptions *options, const string &extractDir, CAfs2Archive *archive, IStream *archiveDataStream, bool_t isInternal, unordered_set &extractedCueIds) { + if (!CFileSystem::DirectoryExists(extractDir)) { + if (!CFileSystem::MkDir(extractDir)) { + fprintf(stderr, "Failed to create directory %s.\n", extractDir.c_str()); + return -1; + } + } + + // Set up key modifier + uint16_t keyModifier; + + if (formatVersion >= CAcbFile::KEY_MODIFIER_ENABLED_VERSION) { + keyModifier = archive->GetHcaKeyModifier(); + } else { + keyModifier = 0; + } + + if (options->callback) { + AcbWalkCallbackParams p; + + p.acb = &acb; + p.archive = archive; + p.archiveDataStream = archiveDataStream; + p.formatVersion = formatVersion; + p.isInternal = isInternal; + p.walkOptions = options; + + p.walkOptions->decoderConfig.cipherConfig.keyModifier = keyModifier; + + // First try to extract files with readable cue names + { + const auto &fileNames = acb.GetFileNames(); + uint32_t i = 0; + + for (const auto &extractFileName : fileNames) { + if (extractFileName.empty()) { + continue; + } + + p.extractPathHint = CPath::Combine(extractDir, extractFileName); + + auto entryDataStream = acb.OpenDataStream(extractFileName.c_str()); + + if (entryDataStream) { + p.entryDataStream = entryDataStream; + + const auto fileRecord = acb.GetFileRecord(extractFileName.c_str()); + + assert(fileRecord != nullptr); + + p.cueInfo.id = fileRecord->cueId; + p.cueInfo.offset = fileRecord->fileOffsetAligned; + p.cueInfo.size = fileRecord->fileSize; + + options->callback(&p); + + extractedCueIds.insert(fileRecord->cueId); + } else { + fprintf(stderr, "Cue #%" PRIu32 " (%s) cannot be retrieved.\n", i + 1, extractFileName.c_str()); + } + + ++i; + p.entryDataStream = nullptr; + p.extractPathHint = ""; + + delete entryDataStream; + } + } + + // Then try on not-yet-exported files + { + const auto &fileList = archive->GetFiles(); + + for (auto &entry : fileList) { + auto &record = entry.second; + + if (extractedCueIds.find(record.cueId) != extractedCueIds.end()) { + continue; + } + + string extractFileName; + + if (options->useCueName) { + extractFileName = acb.GetCueNameFromCueId(record.cueId); + } else { + extractFileName = CAcbFile::GetSymbolicFileNameFromCueId(record.cueId); + } + + auto entryDataStream = CAcbHelper::ExtractToNewStream(archiveDataStream, record.fileOffsetAligned, (uint32_t)record.fileSize); + + p.entryDataStream = entryDataStream; + p.extractPathHint = CPath::Combine(extractDir, extractFileName); + p.cueInfo.id = record.cueId; + p.cueInfo.offset = record.fileOffsetAligned; + p.cueInfo.size = record.fileSize; + + options->callback(&p); + + p.entryDataStream = nullptr; + p.extractPathHint = ""; + p.cueInfo.id = 0; + p.cueInfo.offset = 0; + p.cueInfo.size = 0; + + delete entryDataStream; + } + } + } + + options->decoderConfig.cipherConfig.keyModifier = 0; + + return 0; +} diff --git a/src/apps/common/acbextract.h b/src/apps/common/acbextract.h new file mode 100644 index 0000000..9f892d5 --- /dev/null +++ b/src/apps/common/acbextract.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "../cgssh.h" +#include "../../lib/cgss_api.h" + +struct AcbWalkOptions; +struct AFS2_FILE_RECORD; + +struct AcbWalkCallbackParams { + + AcbWalkCallbackParams(); + + AcbWalkOptions *walkOptions; + cgss::CAcbFile *acb; + uint32_t formatVersion; + std::string extractPathHint; + cgss::CAfs2Archive *archive; + cgss::IStream *archiveDataStream; + cgss::IStream *entryDataStream; + bool_t isInternal; + + struct _cueinfo { + + inline _cueinfo() + : id(0), offset(0), size(0) { + } + + uint32_t id; + uint64_t offset; + uint64_t size; + + } cueInfo; + +}; + +typedef int (*AcbWalkCallback)(AcbWalkCallbackParams *params); + +struct AcbWalkOptions { + + AcbWalkOptions(); + + HCA_DECODER_CONFIG decoderConfig; + bool_t useCueName; + AcbWalkCallback callback; + +}; + +int AcbWalk(const std::string &inputAcbFile, AcbWalkOptions *options); diff --git a/src/lib/ichinose/CAcbFile.cpp b/src/lib/ichinose/CAcbFile.cpp index ecb99c8..2b96ee7 100644 --- a/src/lib/ichinose/CAcbFile.cpp +++ b/src/lib/ichinose/CAcbFile.cpp @@ -161,7 +161,7 @@ void CAcbFile::InitializeCueNameToWaveformTable() { cueName += GetExtensionForEncodeType(cue.encodeType); - strncpy(cue.cueName, cueName.c_str(), ACB_CUE_RECORD_NAME_MAX_LEN); + snprintf(cue.cueName, ACB_CUE_RECORD_NAME_MAX_LEN, "%s", cueName.c_str()); _cueNameToWaveform[cueName] = cue.waveformId; @@ -258,96 +258,103 @@ const vector &CAcbFile::GetFileNames() const { IStream *CAcbFile::OpenDataStream(const char *fileName) { IStream *result = nullptr; - for (auto &cue : _cues) { - if (strcmp(cue.cueName, fileName) == 0) { - result = GetDataStreamFromCueInfo(cue, fileName); - break; - } + const auto cue = GetCueRecord(fileName); + + if (cue) { + const auto file = GetFileRecord(cue); + const auto stream = ChooseSourceStream(cue); + + result = CAcbHelper::ExtractToNewStream(stream, file->fileOffsetAligned, static_cast(file->fileSize)); } return result; } IStream *CAcbFile::OpenDataStream(uint32_t cueId) { - char tempFileName[40] = {0}; + IStream *result = nullptr; - sprintf(tempFileName, "cue #%" PRIu32, cueId); + const auto cue = GetCueRecord(cueId); - IStream *result = nullptr; + if (cue) { + const auto file = GetFileRecord(cue); + const auto stream = ChooseSourceStream(cue); - for (auto &cue : _cues) { - if (cue.cueId == cueId) { - result = GetDataStreamFromCueInfo(cue, tempFileName); - break; - } + result = CAcbHelper::ExtractToNewStream(stream, file->fileOffsetAligned, static_cast(file->fileSize)); } return result; } -IStream *CAcbFile::GetDataStreamFromCueInfo(const ACB_CUE_RECORD &cue, const char *fileNameForError) { - if (!cue.isWaveformIdentified) { - return nullptr; - } +const char *CAcbFile::GetFileName() const { + return _fileName; +} - IStream *result; +string CAcbFile::GetSymbolicFileNameFromCueId(uint32_t cueId) { + char buffer[40] = {0}; - if (cue.isStreaming) { - auto externalAwb = _externalAwb; + sprintf(buffer, "cue_%06" PRIu32 ".bin", cueId); - if (externalAwb == nullptr) { - return nullptr; - } + return string(buffer); +} - auto &files = externalAwb->GetFiles(); - if (files.find(cue.waveformId) == files.end()) { - return nullptr; +string CAcbFile::GetCueNameFromCueId(uint32_t cueId) { + for (auto &cue : _cues) { + if (cue.waveformId == cueId) { + return string(cue.cueName); } + } - auto &file = files.at(cue.waveformId); - - result = CAcbHelper::ExtractToNewStream(externalAwb->GetStream(), file.fileOffsetAligned, static_cast(file.fileSize)); - } else { - auto internalAwb = _internalAwb; + return GetSymbolicFileNameFromCueId(cueId); +} - if (internalAwb == nullptr) { - return nullptr; +const ACB_CUE_RECORD *CAcbFile::GetCueRecord(const char *waveformFileName) { + for (auto &cue : _cues) { + if (strcmp(cue.cueName, waveformFileName) == 0) { + return &cue; } + } - auto &files = internalAwb->GetFiles(); + return nullptr; +} - if (files.find(cue.waveformId) == files.end()) { - return nullptr; +const ACB_CUE_RECORD *CAcbFile::GetCueRecord(uint32_t cueId) { + for (auto &cue : _cues) { + if (cue.cueId == cueId) { + return &cue; } - - auto &file = files.at(cue.waveformId); - - result = CAcbHelper::ExtractToNewStream(GetStream(), file.fileOffsetAligned, static_cast(file.fileSize)); } - return result; + return nullptr; } -const char *CAcbFile::GetFileName() const { - return _fileName; +const AFS2_FILE_RECORD *CAcbFile::GetFileRecord(const char *waveformFileName) { + const auto cue = GetCueRecord(waveformFileName); + + if (cue == nullptr) { + return nullptr; + } + + return GetFileRecord(cue); } -string CAcbFile::GetSymbolicFileNameFromCueId(uint32_t cueId) { - char buffer[40] = {0}; +const AFS2_FILE_RECORD *CAcbFile::GetFileRecord(uint32_t cueId) { + const auto cue = GetCueRecord(cueId); - sprintf(buffer, "dat_%06u.bin", cueId); + if (cue == nullptr) { + return nullptr; + } - return string(buffer); + return GetFileRecord(cue); } -string CAcbFile::GetCueNameFromCueId(uint32_t cueId) { - for (auto &cue : _cues) { +bool_t CAcbFile::IsCueIdentified(uint32_t cueId) { + for (auto &cue: _cues) { if (cue.waveformId == cueId) { - return string(cue.cueName); + return cue.isWaveformIdentified; } } - return GetSymbolicFileNameFromCueId(cueId); + return FALSE; } uint32_t CAcbFile::GetFormatVersion() const { @@ -383,6 +390,61 @@ std::string CAcbFile::FindExternalAwbFileName() { return ""; } +const AFS2_FILE_RECORD *CAcbFile::GetFileRecord(const ACB_CUE_RECORD *cue) { + if (cue == nullptr) { + return nullptr; + } + + if (!cue->isWaveformIdentified) { + return nullptr; + } + + if (cue->isStreaming) { + auto externalAwb = _externalAwb; + + if (externalAwb == nullptr) { + return nullptr; + } + + auto &files = externalAwb->GetFiles(); + if (files.find(cue->waveformId) == files.end()) { + return nullptr; + } + + auto &file = files.at(cue->waveformId); + + return &file; + } else { + auto internalAwb = _internalAwb; + + if (internalAwb == nullptr) { + return nullptr; + } + + auto &files = internalAwb->GetFiles(); + + if (files.find(cue->waveformId) == files.end()) { + return nullptr; + } + + auto &file = files.at(cue->waveformId); + + return &file; + } +} + +IStream *CAcbFile::ChooseSourceStream(const ACB_CUE_RECORD *cue) { + if (cue == nullptr) { + return nullptr; + } + + if (cue->isStreaming) { + return _externalAwb->GetStream(); + } else { + return this->GetStream(); + } +} + static string GetExtensionForEncodeType(uint8_t encodeType) { auto type = static_cast(encodeType); diff --git a/src/lib/ichinose/CAcbFile.h b/src/lib/ichinose/CAcbFile.h index ed2a172..9fee03e 100644 --- a/src/lib/ichinose/CAcbFile.h +++ b/src/lib/ichinose/CAcbFile.h @@ -36,6 +36,16 @@ CGSS_NS_BEGIN std::string GetCueNameFromCueId(uint32_t cueId); + const ACB_CUE_RECORD *GetCueRecord(const char *waveformFileName); + + const ACB_CUE_RECORD *GetCueRecord(uint32_t cueId); + + const AFS2_FILE_RECORD *GetFileRecord(const char *waveformFileName); + + const AFS2_FILE_RECORD *GetFileRecord(uint32_t cueId); + + bool_t IsCueIdentified(uint32_t cueId); + void Initialize() override; CAfs2Archive *GetInternalAwb(); @@ -54,7 +64,9 @@ CGSS_NS_BEGIN void InitializeAwbArchives(); - IStream *GetDataStreamFromCueInfo(const ACB_CUE_RECORD &cue, const char *fileNameForError); + const AFS2_FILE_RECORD *GetFileRecord(const ACB_CUE_RECORD *cue); + + IStream *ChooseSourceStream(const ACB_CUE_RECORD *cue); std::string FindExternalAwbFileName(); diff --git a/src/lib/takamori/exceptions/CException.cpp b/src/lib/takamori/exceptions/CException.cpp index eb1bba9..ea75c5e 100644 --- a/src/lib/takamori/exceptions/CException.cpp +++ b/src/lib/takamori/exceptions/CException.cpp @@ -6,6 +6,11 @@ CGSS_NS_BEGIN : MyClass("") { } + // Intended + CException::CException(const CException &) noexcept + : MyBase(), _result(CGSS_OP_GENERIC_FAULT) { + } + CException::CException(CGSS_OP_RESULT result) noexcept : MyClass(result, "") { } diff --git a/src/lib/takamori/exceptions/CException.h b/src/lib/takamori/exceptions/CException.h index 2a496d3..0643304 100644 --- a/src/lib/takamori/exceptions/CException.h +++ b/src/lib/takamori/exceptions/CException.h @@ -14,7 +14,7 @@ CGSS_NS_BEGIN CException() noexcept; - CException(const CException &) noexcept = default; + CException(const CException &) noexcept; CException& operator=(const CException &) noexcept;