From 3921d675ff408af80b3c25be59ec659c34fa96e7 Mon Sep 17 00:00:00 2001 From: George Wu Date: Wed, 17 Oct 2018 16:43:33 +0200 Subject: [PATCH] Added acb2wavs, and various fixes. --- cmake/project_compile.cmake | 1 + cmake/project_files.cmake | 2 + cmake/project_link.cmake | 1 + cmake/project_postbuild.cmake | 4 + cmake/project_targets.cmake | 1 + src/apps/acb2wavs/acb2wavs.cpp | 286 ++++++++++++++++++ src/lib/capi/cgss_capi.cpp | 2 +- src/lib/cgss_intf.h | 4 + src/lib/ichinose/CAcbFile.cpp | 114 ++++--- src/lib/ichinose/CAcbFile.h | 10 +- src/lib/ichinose/CAfs2Archive.cpp | 15 +- src/lib/ichinose/CAfs2Archive.h | 6 +- src/lib/ichinose/CUtfTable.cpp | 4 - src/lib/ichinose/CUtfTable.h | 2 - src/lib/kawashima/hca/CHcaCipherConfig.cpp | 30 +- src/lib/kawashima/hca/CHcaCipherConfig.h | 2 +- src/lib/kawashima/hca/CHcaCipherConverter.cpp | 4 +- src/lib/kawashima/hca/CHcaDecoder.cpp | 10 +- src/lib/kawashima/hca/CHcaDecoder.h | 12 +- src/lib/kawashima/hca/CHcaFormatReader.cpp | 49 +++ src/lib/kawashima/hca/CHcaFormatReader.h | 18 +- src/lib/kawashima/hca/internal/CHcaCipher.cpp | 55 ++-- src/lib/kawashima/hca/internal/CHcaCipher.h | 8 +- src/lib/takamori/CFileSystem.cpp | 156 ++++++++++ src/lib/takamori/CFileSystem.h | 33 ++ src/lib/takamori/CPath.cpp | 98 ++++++ src/lib/takamori/CPath.h | 31 ++ src/lib/takamori/streams/CFileStream.cpp | 19 +- src/lib/takamori/streams/CFileStream.h | 2 - 29 files changed, 842 insertions(+), 137 deletions(-) create mode 100644 src/apps/acb2wavs/acb2wavs.cpp create mode 100644 src/lib/takamori/CFileSystem.cpp create mode 100644 src/lib/takamori/CFileSystem.h create mode 100644 src/lib/takamori/CPath.cpp create mode 100644 src/lib/takamori/CPath.h diff --git a/cmake/project_compile.cmake b/cmake/project_compile.cmake index 17e3e3d..cda4a8f 100644 --- a/cmake/project_compile.cmake +++ b/cmake/project_compile.cmake @@ -5,6 +5,7 @@ endif () target_compile_definitions(hcacc PRIVATE __COMPILE_WITH_CGSS_KEYS) target_compile_definitions(hcaenc PRIVATE __COMPILE_WITH_CGSS_KEYS) target_compile_definitions(hca2wav PRIVATE __COMPILE_WITH_CGSS_KEYS) +target_compile_definitions(acb2wavs PRIVATE __COMPILE_WITH_CGSS_KEYS) # http://stackoverflow.com/questions/10046114/in-cmake-how-can-i-test-if-the-compiler-is-clang if (MINGW OR CYGWIN) diff --git a/cmake/project_files.cmake b/cmake/project_files.cmake index be1cf76..fc4dadb 100644 --- a/cmake/project_files.cmake +++ b/cmake/project_files.cmake @@ -19,6 +19,8 @@ set(UTFTABLE_SOURCE_FILES src/apps/utftable/utftable.c ${LIBCGSS_API_FILES}) set(ACBUNPACK_SOURCE_FILES src/apps/acbunpack/acbunpack.cpp ${LIBCGSS_API_FILES}) +set(ACB2WAVS_SOURCE_FILES + src/apps/acb2wavs/acb2wavs.cpp ${LIBCGSS_API_FILES}) set(LIBCGSS_DEF_FILE src/cgss.def) set(LIBCGSS_SOURCE_FILES ${LIBCGSS_SOURCE_FILES} ${LIBCGSS_DEF_FILE}) diff --git a/cmake/project_link.cmake b/cmake/project_link.cmake index 64040a7..fb91adc 100644 --- a/cmake/project_link.cmake +++ b/cmake/project_link.cmake @@ -23,3 +23,4 @@ target_link_libraries(hca2wav cgss) target_link_libraries(hcainfo cgss) target_link_libraries(utftable cgss) target_link_libraries(acbunpack cgss) +target_link_libraries(acb2wavs cgss) diff --git a/cmake/project_postbuild.cmake b/cmake/project_postbuild.cmake index df29408..d2d0738 100644 --- a/cmake/project_postbuild.cmake +++ b/cmake/project_postbuild.cmake @@ -25,6 +25,10 @@ if (${GNU_COMPILER}) POST_BUILD COMMAND ${CMAKE_STRIP} -s $ WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + add_custom_command(TARGET acb2wavs + POST_BUILD + COMMAND ${CMAKE_STRIP} -s $ + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) add_custom_command(TARGET cgss POST_BUILD COMMAND ${CMAKE_STRIP} -s $ diff --git a/cmake/project_targets.cmake b/cmake/project_targets.cmake index d7de4f2..29dce97 100644 --- a/cmake/project_targets.cmake +++ b/cmake/project_targets.cmake @@ -4,6 +4,7 @@ add_executable(hca2wav ${HCA2WAV_SOURCE_FILES}) add_executable(hcainfo ${HCAINFO_SOURCE_FILES}) add_executable(utftable ${UTFTABLE_SOURCE_FILES}) add_executable(acbunpack ${ACBUNPACK_SOURCE_FILES}) +add_executable(acb2wavs ${ACB2WAVS_SOURCE_FILES}) add_library(cgss SHARED ${LIBCGSS_SOURCE_FILES}) if (${BUILD_JNI_INTERFACE}) diff --git a/src/apps/acb2wavs/acb2wavs.cpp b/src/apps/acb2wavs/acb2wavs.cpp new file mode 100644 index 0000000..8218c9b --- /dev/null +++ b/src/apps/acb2wavs/acb2wavs.cpp @@ -0,0 +1,286 @@ +#include "../cgssh.h" +#include "../../lib/cgss_api.h" + +#include +#include +#include + +using namespace cgss; +using namespace std; + +void PrintHelp(); + +int ParseArgs(int argc, const char *argv[], const char **input, HCA_CIPHER_CONFIG &cc); + +int DoWork(const string &inputFile, const HCA_CIPHER_CONFIG &cc); + +int ProcessAllBinaries(uint32_t formatVersion, const HCA_DECODER_CONFIG &dc, const string &extractDir, CAfs2Archive *archive, IStream *dataStream, bool_t isInternal); + +int DecodeHca(IStream *hcaDataStream, IStream *waveStream, const HCA_DECODER_CONFIG &dc); + +template +T atoh(const char *str); + +template +T atoh(const char *str, int max_length); + +string ReplaceExtension(const std::string &s, const std::string &oldExt, const std::string &newExt); + +int main(int argc, const char *argv[]) { + if (argc < 2) { + PrintHelp(); + } + + const char *inputFile; + HCA_CIPHER_CONFIG cc = {0}; + + const auto parsed = ParseArgs(argc, argv, &inputFile, cc); + + if (parsed < 0) { + return 0; + } else if (parsed > 0) { + return parsed; + } + + return DoWork(inputFile, cc); +} + +void PrintHelp() { + cout << "Usage:\n" << endl; + cout << "acb2wavs [-a -b ]" << endl; +} + +int ParseArgs(int argc, const char *argv[], const char **input, HCA_CIPHER_CONFIG &cc) { + if (argc < 2) { + PrintHelp(); + return -1; + } + + *input = argv[1]; + + cc.keyModifier = 0; + + for (int i = 2; i < argc; ++i) { + if (argv[i][0] == '-' || argv[i][0] == '/') { + switch (argv[i][1]) { + case 'a': + if (i + 1 < argc) { + cc.keyParts.key1 = atoh(argv[++i]); + } + break; + case 'b': + if (i + 1 < argc) { + cc.keyParts.key2 = atoh(argv[++i]); + } + break; + default: + return 2; + } + } + } + + return 0; +} + +int DoWork(const string &inputFile, const HCA_CIPHER_CONFIG &cc) { + const auto baseExtractDirPath = CPath::Combine(CPath::GetDirectoryName(inputFile), "_acb_" + CPath::GetFileName(inputFile)); + + CFileStream fileStream(inputFile.c_str(), FileMode::OpenExisting, FileAccess::Read); + CAcbFile acb(&fileStream, inputFile.c_str()); + + acb.Initialize(); + + CHcaDecoderConfig dc; + + dc.waveHeaderEnabled = TRUE; + dc.decodeFunc = CDefaultWaveGenerator::Decode16BitS; + dc.cipherConfig = cc; + + CAfs2Archive *archive; + uint32_t formatVersion = acb.GetFormatVersion(); + int r; + + archive = acb.GetInternalAwb(); + + if (archive) { + const auto extractDir = CPath::Combine(baseExtractDirPath, "internal"); + + try { + r = ProcessAllBinaries(formatVersion, dc, 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; + } + } + + archive = acb.GetExternalAwb(); + + if (archive) { + const auto extractDir = CPath::Combine(baseExtractDirPath, "external"); + + try { + CFileStream fs(archive->GetFileName(), FileMode::OpenExisting, FileAccess::Read); + + r = ProcessAllBinaries(formatVersion, dc, extractDir, archive, &fs, TRUE); + } 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; +} + +int ProcessAllBinaries(uint32_t formatVersion, const HCA_DECODER_CONFIG &dc, 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 = dc; + + if (formatVersion >= CAcbFile::KEY_MODIFIER_ENABLED_VERSION) { + decoderConfig.cipherConfig.keyModifier = archive->GetHcaKeyModifier(); + } else { + decoderConfig.cipherConfig.keyModifier = 0; + } + + for (auto &entry : archive->GetFiles()) { + auto &record = entry.second; + auto extractFileName = CAcbFile::GetSymbolicFileNameFromCueId(record.cueId); + extractFileName = ReplaceExtension(extractFileName, ".bin", ".wav"); + + auto extractFilePath = CPath::Combine(extractDir, extractFileName); + + auto fileData = CAcbHelper::ExtractToNewStream(dataStream, record.fileOffsetAligned, (uint32_t)record.fileSize); + + const auto isHca = CHcaFormatReader::IsPossibleHcaStream(fileData); + + fprintf(stdout, "Processing %s AFS: #%u (offset=%u, size=%u)... ", afsSource, (uint32_t)record.cueId, (uint32_t)record.fileOffsetAligned, (uint32_t)record.fileSize); + + int r; + + if (isHca) { + 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()); + } + } else { + fprintf(stdout, "skipped (not HCA)\n"); + } + + delete fileData; + } + + return 0; +} + +int DecodeHca(IStream *hcaDataStream, IStream *waveStream, const HCA_DECODER_CONFIG &dc) { + CHcaDecoder decoder(hcaDataStream, dc); + static const int bufferSize = 10240; + uint8_t buffer[bufferSize]; + uint32_t read = 1; + + while (read > 0) { + read = decoder.Read(buffer, bufferSize, 0, bufferSize); + + if (read > 0) { + waveStream->Write(buffer, bufferSize, 0, read); + } + } + + return 0; +} + +#define IS_NUM(ch) ('0' <= (ch) && (ch) <= '9') +#define IS_UPHEX(ch) ('A' <= (ch) && (ch) <= 'F') +#define IS_LOHEX(ch) ('a' <= (ch) && (ch) <= 'f') + +template +T atoh(const char *str) { + return atoh(str, 8); +} + +template +T atoh(const char *str, int max_length) { + max_length = std::min((size_t)max_length, sizeof(T) * 2); + + int i = 0; + uint32_t ret = 0; + + while (i < max_length && *str) { + if (IS_NUM(*str)) { + ret = (ret << 4) | (uint32_t)(*str - '0'); + } else if (IS_UPHEX(*str)) { + ret = (ret << 4) | (uint32_t)(*str - 'A' + 10); + } else if (IS_LOHEX(*str)) { + ret = (ret << 4) | (uint32_t)(*str - 'a' + 10); + } else { + break; + } + + ++str; + } + + return ret; +} + +// https://stackoverflow.com/a/874160 +bool hasEnding(std::string const &fullString, std::string const &ending) { + if (fullString.length() >= ending.length()) { + return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending)); + } else { + return false; + } +} + +string ReplaceExtension(const std::string &s, const std::string &oldExt, const std::string &newExt) { + if (s.size() < oldExt.size()) { + return s; + } + + auto sl = s; + auto extl = oldExt; + + // ALERT! + // Since the only usage here is replacing extensions, and we promise that the + // extensions are in ASCII, and we don't care about chars before the extension, + // we can use this method (tolower()). Otherwise, it causes trouble for non- + // ASCII encodings. + std::transform(sl.begin(), sl.end(), sl.begin(), ::tolower); + std::transform(extl.begin(), extl.end(), extl.begin(), ::tolower); + + if (!hasEnding(sl, extl)) { + return s; + } + + return s.substr(0, s.size() - oldExt.size()) + newExt; +} diff --git a/src/lib/capi/cgss_capi.cpp b/src/lib/capi/cgss_capi.cpp index b9ad638..b60171a 100644 --- a/src/lib/capi/cgss_capi.cpp +++ b/src/lib/capi/cgss_capi.cpp @@ -87,7 +87,7 @@ CGSS_API_IMPL(bool_t) cgssHelperFileExists(LPCSTR fileName) { return FALSE; } - return CFileStream::FileExists(fileName); + return CFileSystem::FileExists(fileName); } CGSS_API_IMPL(CGSS_OP_RESULT) cgssStreamRead(CGSS_HANDLE handle, void *buffer, uint32_t bufferSize, size_t offset, uint32_t count, _OUT_ uint32_t *read) { diff --git a/src/lib/cgss_intf.h b/src/lib/cgss_intf.h index f6a9f1d..470994d 100644 --- a/src/lib/cgss_intf.h +++ b/src/lib/cgss_intf.h @@ -16,6 +16,10 @@ #include "takamori/streams/CBinaryReader.h" #include "takamori/streams/CBinaryWriter.h" +#include "takamori/CBitConverter.h" +#include "takamori/CPath.h" +#include "takamori/CFileSystem.h" + #include "kawashima/hca/CHcaFormatReader.h" #include "kawashima/hca/CDefaultWaveGenerator.h" #include "kawashima/hca/CHcaDecoder.h" diff --git a/src/lib/ichinose/CAcbFile.cpp b/src/lib/ichinose/CAcbFile.cpp index 3d05b12..d37b9c5 100644 --- a/src/lib/ichinose/CAcbFile.cpp +++ b/src/lib/ichinose/CAcbFile.cpp @@ -1,3 +1,4 @@ +#include #include "../cgss_cenum.h" #include "CAfs2Archive.h" #include "../cgss_cdata.h" @@ -5,25 +6,28 @@ #include "../takamori/exceptions/CFormatException.h" #include "../takamori/streams/CBinaryReader.h" #include "../takamori/streams/CFileStream.h" +#include "../takamori/CFileSystem.h" #include "../takamori/streams/CMemoryStream.h" +#include "../takamori/CPath.h" #include "CAcbHelper.h" #include "CAcbFile.h" using namespace cgss; +using namespace std; -static std::string GetExtensionForEncodeType(uint8_t encodeType); +static string GetExtensionForEncodeType(uint8_t encodeType); template bool_t GetFieldValueAsNumber(CUtfTable *table, uint32_t rowIndex, const char *fieldName, T *result); -bool_t GetFieldValueAsString(CUtfTable *table, uint32_t rowIndex, const char *fieldName, std::string &s); +bool_t GetFieldValueAsString(CUtfTable *table, uint32_t rowIndex, const char *fieldName, string &s); + +const uint32_t CAcbFile::KEY_MODIFIER_ENABLED_VERSION = 0x01300000; CAcbFile::CAcbFile(cgss::IStream *stream, const char *fileName) : MyClass(stream, 0, fileName) { } -const uint32_t CAcbFile::KEY_MODIFIER_ENABLED_VERSION = 0x01300000; - CAcbFile::CAcbFile(IStream *stream, uint64_t streamOffset, const char *fileName) : MyBase(stream, streamOffset) { _internalAwb = nullptr; @@ -61,12 +65,9 @@ void CAcbFile::InitializeAcbTables() { _cues.reserve(cueCount); - uint64_t - refItemOffset = 0; - uint32_t - refItemSize = 0, refCorrection = 0; - CBinaryReader - reader(GetStream()); + uint64_t refItemOffset = 0; + uint32_t refItemSize = 0, refCorrection = 0; + CBinaryReader reader(GetStream()); for (uint32_t i = 0; i < cueCount; ++i) { ACB_CUE_RECORD cue = {0}; @@ -99,8 +100,7 @@ void CAcbFile::InitializeAcbTables() { if (refItemSize != 0) { cue.waveformIndex = reader.PeekUInt16BE(refItemOffset + refCorrection); - uint8_t - isStreaming; + uint8_t isStreaming; auto hasIsStreaming = GetFieldValueAsNumber(waveformTable, cue.waveformIndex, "Streaming", &isStreaming); if (hasIsStreaming) { @@ -122,8 +122,7 @@ void CAcbFile::InitializeAcbTables() { } } - uint8_t - encodeType; + uint8_t encodeType; if (GetFieldValueAsNumber(waveformTable, cue.waveformIndex, "EncodeType", &encodeType)) { cue.encodeType = encodeType; } @@ -151,7 +150,7 @@ void CAcbFile::InitializeCueNameToWaveformTable() { auto &cue = _cues[cueIndex]; if (cue.isWaveformIdentified) { - std::string cueName; + string cueName; if (!GetFieldValueAsString(cueNameTable, i, "CueName", cueName)) { continue; @@ -169,21 +168,19 @@ void CAcbFile::InitializeCueNameToWaveformTable() { } void CAcbFile::InitializeAwbArchives() { - uint32_t - internalAwbSize; + uint32_t internalAwbSize; if (GetFieldSize(0, "AwbFile", &internalAwbSize) && internalAwbSize > 0) { _internalAwb = GetInternalAwb(); } - uint32_t - externalAwbSize; + uint32_t externalAwbSize; if (GetFieldSize(0, "StreamAwbAfs2Header", &externalAwbSize) && externalAwbSize > 0) { _externalAwb = GetExternalAwb(); } } CUtfTable *CAcbFile::GetTable(const char *tableName) { - std::string s(tableName); + string s(tableName); CUtfTable *table; @@ -201,15 +198,13 @@ CUtfTable *CAcbFile::GetTable(const char *tableName) { } CUtfTable *CAcbFile::ResolveTable(const char *tableName) { - uint64_t - tableOffset; + uint64_t tableOffset; if (!GetFieldOffset(0, tableName, &tableOffset)) { return nullptr; } - uint32_t - tableSize; + uint32_t tableSize; if (!GetFieldSize(0, tableName, &tableSize)) { return nullptr; @@ -221,8 +216,7 @@ CUtfTable *CAcbFile::ResolveTable(const char *tableName) { } CAfs2Archive *CAcbFile::GetInternalAwb() { - uint64_t - internalAwbOffset; + uint64_t internalAwbOffset; if (!GetFieldOffset(0, "AwbFile", &internalAwbOffset)) { return nullptr; @@ -234,16 +228,26 @@ CAfs2Archive *CAcbFile::GetInternalAwb() { } CAfs2Archive *CAcbFile::GetExternalAwb() { - // TODO - return nullptr; + const auto extAwbFileName = FindExternalAwbFileName(); + + if (extAwbFileName.empty()) { + return nullptr; + } + + // Checksum checking is skipped. + + const auto fs = new CFileStream(extAwbFileName.c_str(), FileMode::OpenExisting, FileAccess::Read); + const auto archive = new CAfs2Archive(fs, 0, extAwbFileName.c_str(), TRUE); + + return archive; } -const std::vector &CAcbFile::GetFileNames() const { +const vector &CAcbFile::GetFileNames() const { return _fileNames; } IStream *CAcbFile::OpenDataStream(const char *fileName) { - IStream * result = nullptr; + IStream *result = nullptr; for (auto &cue : _cues) { if (strcmp(cue.cueName, fileName) == 0) { @@ -260,7 +264,7 @@ IStream *CAcbFile::OpenDataStream(uint32_t cueId) { sprintf(tempFileName, "cue #%u", cueId); - IStream * result = nullptr; + IStream *result = nullptr; for (auto &cue : _cues) { if (cue.cueId == cueId) { @@ -277,7 +281,7 @@ IStream *CAcbFile::GetDataStreamFromCueInfo(const ACB_CUE_RECORD &cue, const cha return nullptr; } - IStream * result; + IStream *result; if (cue.isStreaming) { auto externalAwb = _externalAwb; @@ -293,8 +297,7 @@ IStream *CAcbFile::GetDataStreamFromCueInfo(const ACB_CUE_RECORD &cue, const cha auto &file = files.at(cue.waveformId); - CFileStream - fs(file.fileName, FileMode::OpenExisting, FileAccess::Read); + CFileStream fs(file.fileName, FileMode::OpenExisting, FileAccess::Read); result = CAcbHelper::ExtractToNewStream(&fs, file.fileOffsetAligned, static_cast(file.fileSize)); } else { @@ -322,17 +325,46 @@ const char *CAcbFile::GetFileName() const { return _fileName; } -std::string CAcbFile::GetSymbolicFileNameFromCueId(uint32_t cueId) { +string CAcbFile::GetSymbolicFileNameFromCueId(uint32_t cueId) { char buffer[40] = {0}; sprintf(buffer, "dat_%06u.bin", cueId); - return std::string(buffer); + return string(buffer); } uint32_t CAcbFile::GetFormatVersion() const { return _formatVersion; } -static std::string GetExtensionForEncodeType(uint8_t encodeType) { +std::string CAcbFile::FindExternalAwbFileName() { + const string acbFileName = _fileName; + const auto awbDirPath = CPath::GetDirectoryName(acbFileName); + + auto awbBaseFileName = CPath::GetFileNameWithoutExtension(acbFileName); + + awbBaseFileName = CPath::Combine(awbDirPath, awbBaseFileName); + + auto test = awbBaseFileName + "_streamfiles.awb"; + + if (CFileSystem::FileExists(test.c_str())) { + return test; + } + + test = awbBaseFileName + ".awb"; + + if (CFileSystem::FileExists(test.c_str())) { + return test; + } + + test = awbBaseFileName + "_STR.awb"; + + if (CFileSystem::FileExists(test.c_str())) { + return test; + } + + return ""; +} + +static string GetExtensionForEncodeType(uint8_t encodeType) { auto type = static_cast(encodeType); switch (type) { @@ -351,9 +383,9 @@ static std::string GetExtensionForEncodeType(uint8_t encodeType) { } char buffer[20] = {0}; - sprintf(buffer, ".et-%d.bin", static_cast (encodeType)); + sprintf(buffer, ".et-%d.bin", static_cast(encodeType)); - return std::string(buffer); + return string(buffer); } template @@ -416,7 +448,7 @@ bool_t GetFieldValueAsNumber(CUtfTable *table, uint32_t rowIndex, const char *fi return FALSE; } -bool_t GetFieldValueAsString(CUtfTable *table, uint32_t rowIndex, const char *fieldName, std::string &s) { +bool_t GetFieldValueAsString(CUtfTable *table, uint32_t rowIndex, const char *fieldName, string &s) { auto &rows = table->GetRows(); if (rowIndex >= rows.size()) { @@ -428,7 +460,7 @@ bool_t GetFieldValueAsString(CUtfTable *table, uint32_t rowIndex, const char *fi for (auto &field : row.fields) { if ( strcmp(fieldName, field->name) == 0) { - s = std::string(field->value.str); + s = string(field->value.str); return TRUE; } diff --git a/src/lib/ichinose/CAcbFile.h b/src/lib/ichinose/CAcbFile.h index c1489dc..1069986 100644 --- a/src/lib/ichinose/CAcbFile.h +++ b/src/lib/ichinose/CAcbFile.h @@ -36,6 +36,10 @@ CGSS_NS_BEGIN void Initialize() override; + CAfs2Archive *GetInternalAwb(); + + CAfs2Archive *GetExternalAwb(); + uint32_t GetFormatVersion() const; static const uint32_t KEY_MODIFIER_ENABLED_VERSION; @@ -50,6 +54,8 @@ CGSS_NS_BEGIN IStream *GetDataStreamFromCueInfo(const ACB_CUE_RECORD &cue, const char *fileNameForError); + std::string FindExternalAwbFileName(); + /** * You do not need to manually delete the pointer retrieved. * @param tableName @@ -58,10 +64,6 @@ CGSS_NS_BEGIN CUtfTable *ResolveTable(const char *tableName); - CAfs2Archive *GetInternalAwb(); - - CAfs2Archive *GetExternalAwb(); - CAfs2Archive *_internalAwb; CAfs2Archive *_externalAwb; diff --git a/src/lib/ichinose/CAfs2Archive.cpp b/src/lib/ichinose/CAfs2Archive.cpp index 1449d34..6d0d841 100644 --- a/src/lib/ichinose/CAfs2Archive.cpp +++ b/src/lib/ichinose/CAfs2Archive.cpp @@ -12,9 +12,13 @@ static const int32_t InvalidCueId = -1; CAfs2Archive::CAfs2Archive(cgss::IStream *stream, uint64_t offset, const char *fileName, bool_t disposeStream) { _stream = stream; _streamOffset = offset; - _fileName = fileName; _disposeStream = disposeStream; + const auto fileNameLength = strlen(fileName); + _fileName = new char[fileNameLength + 1]; + memset(_fileName, 0, fileNameLength + 1); + strncpy(_fileName, fileName, fileNameLength); + Initialize(); } @@ -23,6 +27,11 @@ CAfs2Archive::~CAfs2Archive() { delete _stream; _stream = nullptr; } + + if (_fileName) { + delete[] _fileName; + _fileName = nullptr; + } } bool_t CAfs2Archive::IsAfs2Archive(IStream *stream, uint64_t offset) { @@ -118,3 +127,7 @@ uint32_t CAfs2Archive::GetByteAlignment() const { uint16_t CAfs2Archive::GetHcaKeyModifier() const { return _hcaKeyModifier; } + +const char *CAfs2Archive::GetFileName() const { + return _fileName; +} diff --git a/src/lib/ichinose/CAfs2Archive.h b/src/lib/ichinose/CAfs2Archive.h index e7c85d7..4595f76 100644 --- a/src/lib/ichinose/CAfs2Archive.h +++ b/src/lib/ichinose/CAfs2Archive.h @@ -8,7 +8,7 @@ CGSS_NS_BEGIN struct IStream; - class CAfs2Archive final { + class CGSS_EXPORT CAfs2Archive final { __root_class(CAfs2Archive); @@ -28,13 +28,15 @@ CGSS_NS_BEGIN uint16_t GetHcaKeyModifier() const; + const char *GetFileName() const; + private: void Initialize(); IStream *_stream; uint64_t _streamOffset; - const char *_fileName; + char *_fileName; bool_t _disposeStream; std::map _files; diff --git a/src/lib/ichinose/CUtfTable.cpp b/src/lib/ichinose/CUtfTable.cpp index 0c9aeae..bc37761 100644 --- a/src/lib/ichinose/CUtfTable.cpp +++ b/src/lib/ichinose/CUtfTable.cpp @@ -449,8 +449,4 @@ CGSS_NS_BEGIN return FALSE; } - IStream *CUtfTable::GetStream() { - return _stream; - } - CGSS_NS_END diff --git a/src/lib/ichinose/CUtfTable.h b/src/lib/ichinose/CUtfTable.h index 1be794d..d7787c3 100644 --- a/src/lib/ichinose/CUtfTable.h +++ b/src/lib/ichinose/CUtfTable.h @@ -50,8 +50,6 @@ CGSS_NS_BEGIN CUtfReader *GetReader() const; - IStream *GetStream(); - std::vector _rows; virtual void Initialize(); diff --git a/src/lib/kawashima/hca/CHcaCipherConfig.cpp b/src/lib/kawashima/hca/CHcaCipherConfig.cpp index b62a08c..e6b79ba 100644 --- a/src/lib/kawashima/hca/CHcaCipherConfig.cpp +++ b/src/lib/kawashima/hca/CHcaCipherConfig.cpp @@ -1,20 +1,5 @@ #include "CHcaCipherConfig.h" -static void TransformKey(uint32_t key1, uint32_t key2, uint16_t mod, uint32_t *pk1, uint32_t *pk2) { - auto key = (uint64_t)key1 << 32 | key2; - auto k2 = ((uint64_t)mod << 16 | (uint16_t)(~mod + 2)); - - auto newKey = key * k2; - - if (pk1) { - *pk1 = (uint32_t)(newKey >> 32); - } - - if (pk2) { - *pk2 = (uint32_t)(newKey & 0xffffffff); - } -} - CGSS_NS_BEGIN CHcaCipherConfig::CHcaCipherConfig() @@ -33,7 +18,7 @@ CGSS_NS_BEGIN CHcaCipherConfig::CHcaCipherConfig(uint32_t key1, uint32_t key2) : MyClass() { - Initialize(key1, key2); + Initialize(key1, key2, 0); } CHcaCipherConfig::CHcaCipherConfig(uint64_t key) @@ -42,22 +27,14 @@ CGSS_NS_BEGIN CHcaCipherConfig::CHcaCipherConfig(uint32_t key1, uint32_t key2, uint16_t keyModifier) : MyClass() { - if ((key1 == 0 && key2 == 0) || keyModifier == 0) { - Initialize(key1, key2); - } else { - uint32_t newKey1, newKey2; - - TransformKey(key1, key2, keyModifier, &newKey1, &newKey2); - - Initialize(newKey1, newKey2); - } + Initialize(key1, key2, keyModifier); } CHcaCipherConfig::CHcaCipherConfig(uint64_t key, uint16_t keyModifier) : MyClass((uint32_t)(key >> 32), (uint32_t)(key & 0xffffffff), keyModifier) { } - void CHcaCipherConfig::Initialize(uint32_t key1, uint32_t key2) { + void CHcaCipherConfig::Initialize(uint32_t key1, uint32_t key2, uint16_t keyModifier) { if (key1 == 0 && key2 == 0) { cipherType = static_cast(HcaCipherType::NoCipher); } else { @@ -65,6 +42,7 @@ CGSS_NS_BEGIN } keyParts.key1 = key1; keyParts.key2 = key2; + this->keyModifier = keyModifier; } CGSS_NS_END diff --git a/src/lib/kawashima/hca/CHcaCipherConfig.h b/src/lib/kawashima/hca/CHcaCipherConfig.h index daa1769..b71f1d4 100644 --- a/src/lib/kawashima/hca/CHcaCipherConfig.h +++ b/src/lib/kawashima/hca/CHcaCipherConfig.h @@ -28,7 +28,7 @@ CGSS_NS_BEGIN private: - void Initialize(uint32_t key1, uint32_t key2); + void Initialize(uint32_t key1, uint32_t key2, uint16_t keyModifier); }; diff --git a/src/lib/kawashima/hca/CHcaCipherConverter.cpp b/src/lib/kawashima/hca/CHcaCipherConverter.cpp index 5211043..23299ed 100644 --- a/src/lib/kawashima/hca/CHcaCipherConverter.cpp +++ b/src/lib/kawashima/hca/CHcaCipherConverter.cpp @@ -63,8 +63,8 @@ CGSS_NS_BEGIN auto &ccFrom = _ccFrom; const auto &ccTo = _ccTo; ccFrom.cipherType = hcaInfo.cipherType; - _cipherFrom = new CHcaCipher(static_cast(ccFrom.cipherType), ccFrom.keyParts.key1, ccFrom.keyParts.key2); - _cipherTo = new CHcaCipher(static_cast(ccTo.cipherType), ccTo.keyParts.key1, ccTo.keyParts.key2); + _cipherFrom = new CHcaCipher(ccFrom); + _cipherTo = new CHcaCipher(ccTo); } const uint8_t *CHcaCipherConverter::ConvertHeader() { diff --git a/src/lib/kawashima/hca/CHcaDecoder.cpp b/src/lib/kawashima/hca/CHcaDecoder.cpp index a6dad7d..33a4577 100644 --- a/src/lib/kawashima/hca/CHcaDecoder.cpp +++ b/src/lib/kawashima/hca/CHcaDecoder.cpp @@ -18,11 +18,11 @@ CGSS_NS_BEGIN CHcaDecoder::CHcaDecoder(IStream *stream) - : MyClass(stream, HCA_DECODER_CONFIG()) { + : MyClass(stream, HCA_DECODER_CONFIG()) { } CHcaDecoder::CHcaDecoder(IStream *stream, const HCA_DECODER_CONFIG &decoderConfig) - : MyBase(stream) { + : MyBase(stream) { _cipher = nullptr; _ath = nullptr; for (auto i = 0; i < ChannelCount; ++i) { @@ -74,7 +74,7 @@ CGSS_NS_BEGIN } auto &cipherConfig = _decoderConfig.cipherConfig; cipherConfig.cipherType = hcaInfo.cipherType; - _cipher = new CHcaCipher(static_cast(cipherConfig.cipherType), cipherConfig.keyParts.key1, cipherConfig.keyParts.key2); + _cipher = new CHcaCipher(cipherConfig); // Prepare the channel decoders. memset(_channels, 0, sizeof(_channels)); @@ -189,8 +189,8 @@ CGSS_NS_BEGIN wavNote.noteSize += 4 - (wavNote.noteSize & 3); } } - wavData.dataSize = wavRiff.fmtSamplingSize * (hcaInfo.blockCount * 0x80 * 8 + - (wavSmpl.loopEnd - wavSmpl.loopStart) * _decoderConfig.loopCount); + wavData.dataSize = wavRiff.fmtSamplingSize * (hcaInfo.blockCount * 0x80 * 8 + + (wavSmpl.loopEnd - wavSmpl.loopStart) * _decoderConfig.loopCount); wavRiff.riffSize = static_cast(0x1C + ((hcaInfo.loopExists && !WaveSettings::SoftLoop) ? sizeof(wavSmpl) : 0) + (hcaInfo.commentLength > 0 ? 8 + wavNote.noteSize : 0) + sizeof(wavData) + wavData.dataSize); diff --git a/src/lib/kawashima/hca/CHcaDecoder.h b/src/lib/kawashima/hca/CHcaDecoder.h index 3becc06..0911b59 100644 --- a/src/lib/kawashima/hca/CHcaDecoder.h +++ b/src/lib/kawashima/hca/CHcaDecoder.h @@ -22,20 +22,20 @@ CGSS_NS_BEGIN CHcaDecoder(IStream *stream, const HCA_DECODER_CONFIG &decoderConfig); + CHcaDecoder(const CHcaDecoder &) = delete; + virtual ~CHcaDecoder(); - virtual uint32_t Read(void *buffer, uint32_t bufferSize, size_t offset, uint32_t count) override; + uint32_t Read(void *buffer, uint32_t bufferSize, size_t offset, uint32_t count) override; - virtual uint64_t GetPosition() override; + uint64_t GetPosition() override; - virtual void SetPosition(uint64_t value) override; + void SetPosition(uint64_t value) override; - virtual uint64_t GetLength() override; + uint64_t GetLength() override; private: - CHcaDecoder(const CHcaDecoder &) = delete; - void InitializeExtra(); /** diff --git a/src/lib/kawashima/hca/CHcaFormatReader.cpp b/src/lib/kawashima/hca/CHcaFormatReader.cpp index c80b81b..27566bc 100644 --- a/src/lib/kawashima/hca/CHcaFormatReader.cpp +++ b/src/lib/kawashima/hca/CHcaFormatReader.cpp @@ -10,6 +10,35 @@ CGSS_NS_BEGIN + class NullHcaReader final : public CHcaFormatReader { + + __extends(CHcaFormatReader, NullHcaReader); + + public: + + explicit NullHcaReader(IStream *baseStream) + : MyBase(baseStream) { + } + + private: + + uint32_t Read(void *buffer, uint32_t bufferSize, size_t offset, uint32_t count) override { + return 0; + } + + uint64_t GetPosition() override { + return 0; + } + + void SetPosition(uint64_t value) override { + } + + uint64_t GetLength() override { + return 0; + } + + }; + CHcaFormatReader::CHcaFormatReader(IStream *baseStream) : _baseStream(baseStream) { memset(&_hcaInfo, 0, sizeof(HCA_INFO)); @@ -339,4 +368,24 @@ CGSS_NS_BEGIN cout << " Volume adjustment: " << hcaInfo.rvaVolume << endl; } + bool_t CHcaFormatReader::IsPossibleHcaStream(IStream *stream) { + if (!stream) { + return FALSE; + } + + const auto pos = stream->GetPosition(); + + try { + NullHcaReader reader(stream); + } catch (CException &ex) { + return FALSE; + } catch (std::runtime_error &err) { + return FALSE; + } + + stream->SetPosition(pos); + + return TRUE; + } + CGSS_NS_END diff --git a/src/lib/kawashima/hca/CHcaFormatReader.h b/src/lib/kawashima/hca/CHcaFormatReader.h index 64ac6f0..8c8d941 100644 --- a/src/lib/kawashima/hca/CHcaFormatReader.h +++ b/src/lib/kawashima/hca/CHcaFormatReader.h @@ -14,6 +14,8 @@ CGSS_NS_BEGIN explicit CHcaFormatReader(IStream *baseStream); + CHcaFormatReader(const CHcaFormatReader &) = delete; + virtual ~CHcaFormatReader(); /** @@ -27,17 +29,19 @@ CGSS_NS_BEGIN const HCA_INFO GetHcaInfo() const; - virtual uint32_t Write(const void *buffer, uint32_t bufferSize, size_t offset, uint32_t count) override final; + uint32_t Write(const void *buffer, uint32_t bufferSize, size_t offset, uint32_t count) final; + + bool_t IsWritable() const final; - virtual bool_t IsWritable() const override final; + bool_t IsReadable() const final; - virtual bool_t IsReadable() const override final; + bool_t IsSeekable() const final; - virtual bool_t IsSeekable() const override final; + void SetLength(uint64_t value) final; - virtual void SetLength(uint64_t value) override final; + void Flush() final; - virtual void Flush() override final; + static bool_t IsPossibleHcaStream(IStream *stream); protected: @@ -49,8 +53,6 @@ CGSS_NS_BEGIN private: - CHcaFormatReader(const CHcaFormatReader &) = delete; - void Initialize(); void PrintHcaInfo(); diff --git a/src/lib/kawashima/hca/internal/CHcaCipher.cpp b/src/lib/kawashima/hca/internal/CHcaCipher.cpp index 7a28ca8..2783dcc 100644 --- a/src/lib/kawashima/hca/internal/CHcaCipher.cpp +++ b/src/lib/kawashima/hca/internal/CHcaCipher.cpp @@ -1,13 +1,34 @@ #include "CHcaCipher.h" +#include "../../../cgss_cdata.h" + +static void TransformKey(uint32_t key1, uint32_t key2, uint16_t mod, uint32_t *pk1, uint32_t *pk2) { + auto key = (uint64_t)key2 << 32 | key1; + auto k2 = ((uint64_t)mod << 16 | (uint16_t)(~mod + 2)); + + auto newKey = key * k2; + + if (pk2) { + *pk2 = (uint32_t)(newKey >> 32); + } + + if (pk1) { + *pk1 = (uint32_t)(newKey & 0xffffffff); + } +} CGSS_NS_BEGIN CHcaCipher::CHcaCipher() { - Init(HcaCipherType::NoCipher, 0, 0); + Init(CHcaCipherConfig(HcaCipherType::NoCipher)); + } + + CHcaCipher::CHcaCipher(const HCA_CIPHER_CONFIG &config) { + CHcaCipherConfig cfg(config.key, config.keyModifier); + Init(cfg); } - CHcaCipher::CHcaCipher(HcaCipherType type, uint32_t key1, uint32_t key2) { - Init(type, key1, key2); + CHcaCipher::CHcaCipher(const CHcaCipherConfig &config) { + Init(config); } CHcaCipher::CHcaCipher(const CHcaCipher &other) { @@ -16,10 +37,19 @@ CGSS_NS_BEGIN memcpy(_encryptTable, other._encryptTable, TableSize); } - bool_t CHcaCipher::Init(HcaCipherType type, uint32_t key1, uint32_t key2) { - if (!(key1 | key2) && type == HcaCipherType::WithKey) { + bool_t CHcaCipher::Init(const CHcaCipherConfig &config) { + auto type = static_cast(config.cipherType); + + if (!(config.keyParts.key1 | config.keyParts.key2) && type == HcaCipherType::WithKey) { type = HcaCipherType::NoCipher; } + + uint32_t key1 = config.keyParts.key1, key2 = config.keyParts.key2; + + if ((key1 && key2) && type == HcaCipherType::WithKey && config.keyModifier) { + TransformKey(key1, key2, config.keyModifier, &key1, &key2); + } + switch (type) { case HcaCipherType::NoCipher: Init0(); @@ -31,7 +61,9 @@ CGSS_NS_BEGIN Init56(key1, key2); break; } + _cipherType = type; + return InitEncryptTable(); } @@ -125,20 +157,9 @@ CGSS_NS_BEGIN } bool_t CHcaCipher::InitEncryptTable() { - bool_t b; memset(_encryptTable, 0, TableSize); for (uint32_t i = 0; i < TableSize; ++i) { - b = FALSE; - for (uint32_t j = 0; j < TableSize; ++j) { - if (_decryptTable[j] == i) { - _encryptTable[i] = (uint8_t)(j & 0xFF); - b = TRUE; - break; - } - } - if (!b) { - return FALSE; - } + _encryptTable[_decryptTable[i]] = (uint8_t)i; } return TRUE; } diff --git a/src/lib/kawashima/hca/internal/CHcaCipher.h b/src/lib/kawashima/hca/internal/CHcaCipher.h index 43b63b8..d5ddacd 100644 --- a/src/lib/kawashima/hca/internal/CHcaCipher.h +++ b/src/lib/kawashima/hca/internal/CHcaCipher.h @@ -2,6 +2,8 @@ #include "../../../cgss_env.h" #include "../../../cgss_enum.h" +#include "../../../cdata/HCA_CIPHER_CONFIG.h" +#include "../CHcaCipherConfig.h" CGSS_NS_BEGIN @@ -11,7 +13,9 @@ CGSS_NS_BEGIN CHcaCipher(); - CHcaCipher(HcaCipherType type, uint32_t key1, uint32_t key2); + CHcaCipher(const HCA_CIPHER_CONFIG &config); + + CHcaCipher(const CHcaCipherConfig &config); CHcaCipher(const CHcaCipher &); @@ -23,7 +27,7 @@ CGSS_NS_BEGIN private: - bool_t Init(HcaCipherType type, uint32_t key1, uint32_t key2); + bool_t Init(const CHcaCipherConfig &config); static const uint32_t TableSize = 0x100; diff --git a/src/lib/takamori/CFileSystem.cpp b/src/lib/takamori/CFileSystem.cpp new file mode 100644 index 0000000..290a0f9 --- /dev/null +++ b/src/lib/takamori/CFileSystem.cpp @@ -0,0 +1,156 @@ +#include "../cgss_env.h" + +#ifdef __CGSS_OS_WINDOWS__ + +#include + +#else + +#include +#include +#include +#include +#include + +#endif + +#include "CFileSystem.h" + +using namespace cgss; + +bool_t CFileSystem::FileExists(const std::string &path) { + return FileExists(path.c_str()); +} + +bool_t CFileSystem::DirectoryExists(const std::string &path) { + return DirectoryExists(path.c_str()); +} + +bool_t CFileSystem::FileExists(const char *path) { + auto fp = fopen(path, "r"); + + if (!fp) { + return FALSE; + } + + fclose(fp); + + return TRUE; +} + +bool_t CFileSystem::MkDir(const std::string &path) { + return MkDir(path.c_str()); +} + +bool_t CFileSystem::RmFile(const std::string &path) { + return RmFile(path.c_str()); +} + +bool_t CFileSystem::RmFile(const char *path) { + const auto success = remove(path) == 0; + return static_cast(success); +} + +#ifdef __CGSS_OS_WINDOWS__ + +bool_t CFileSystem::DirectoryExists(const char *path) { + const auto type = GetFileAttributes(path); + + if (type == INVALID_FILE_ATTRIBUTES) { + // Cannot open the entry + return FALSE; + } else if (type == FILE_ATTRIBUTE_DIRECTORY) { + // Everything is OK + return TRUE; + } else { + // It is not a directory + return FALSE; + } +} + +// http://blog.nuclex-games.com/2012/06/how-to-create-directories-recursively-with-win32/ +bool_t CFileSystem::MkDir(const char *path) { + static const std::string separators("\\/"); + const std::string directory(path); + + DWORD fileAttributes = ::GetFileAttributes(directory.c_str()); + + if (fileAttributes == INVALID_FILE_ATTRIBUTES) { + + std::size_t slashIndex = directory.find_last_of(separators); + + if (slashIndex != std::wstring::npos) { + MkDir(directory.substr(0, slashIndex)); + } + + BOOL result = ::CreateDirectory(directory.c_str(), nullptr); + + if (result == FALSE) { + return FALSE; + } + } else { + bool isDirectoryOrJunction = + ((fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) || + ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0); + + if (!isDirectoryOrJunction) { + // There is a file with the same name. + return FALSE; + } + } + + return TRUE; +} + +#else + +bool_t CFileSystem::DirectoryExists(const char *path) { + struct stat info {}; + + if (::stat(path, &info) != 0) { + // Cannot open the entry + return FALSE; + } else if (info.st_mode & S_IFDIR) { + // Everything is OK + return TRUE; + } else { + // It is not a directory + return FALSE; + } +} + +// http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html +bool_t CFileSystem::MkDir(const char *path) { + if (path == nullptr) { + return FALSE; + } + + if (strlen(path) == 0) { + return FALSE; + } + + char tmp[256]; + char *p = nullptr; + size_t len; + + strncpy(tmp, path, sizeof(tmp)); + + len = strlen(tmp); + + if (tmp[len - 1] == '/') { + tmp[len - 1] = 0; + } + + for (p = tmp + 1; *p; p++) + if (*p == '/') { + *p = 0; + mkdir(tmp, S_IRWXU); + *p = '/'; + } + + mkdir(tmp, S_IRWXU); + + return TRUE; +} + +#endif diff --git a/src/lib/takamori/CFileSystem.h b/src/lib/takamori/CFileSystem.h new file mode 100644 index 0000000..507c5d8 --- /dev/null +++ b/src/lib/takamori/CFileSystem.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include "../cgss_env.h" + +CGSS_NS_BEGIN + + class CGSS_EXPORT CFileSystem final { + + PURE_STATIC(CFileSystem); + + public: + + static bool_t FileExists(const std::string &path); + + static bool_t DirectoryExists(const std::string &path); + + static bool_t FileExists(const char *path); + + static bool_t DirectoryExists(const char *path); + + static bool_t MkDir(const std::string &path); + + static bool_t MkDir(const char *path); + + static bool_t RmFile(const std::string &path); + + static bool_t RmFile(const char *path); + + }; + +CGSS_NS_END diff --git a/src/lib/takamori/CPath.cpp b/src/lib/takamori/CPath.cpp new file mode 100644 index 0000000..a285dba --- /dev/null +++ b/src/lib/takamori/CPath.cpp @@ -0,0 +1,98 @@ +#include +#include "CPath.h" + +using namespace std; +using namespace cgss; + +string CPath::Combine(const string &basePath, const string &path1) { + if (basePath.empty()) { + return path1; + } + + if (path1.empty()) { + return basePath; + } + + const auto lastBaseChar = basePath[basePath.size() - 1]; + const auto firstAppendChar = path1[0]; + + if ((lastBaseChar != '/' && lastBaseChar != '\\') && (firstAppendChar != '/' && firstAppendChar != '\\')) { + return basePath + "/" + path1; + } else { + return basePath + path1; + } +} + +string CPath::GetExtension(const string &path) { + const auto dotPos = path.rfind('.'); + + if (dotPos == string::npos) { + return ""; + } + + if (dotPos == path.size() - 1) { + return ""; + } + + const auto slashPos = FindLastSlash(path); + + if (slashPos != string::npos) { + if (dotPos < slashPos) { + return ""; + } + } + + // Including the dot + return path.substr(dotPos); +} + +string CPath::GetFileName(const string &path) { + const auto slashPos = FindLastSlash(path); + + if (slashPos == string::npos) { + return path; + } + + if (slashPos == path.size() - 1) { + return ""; + } + + return path.substr(slashPos + 1u); +} + +std::string CPath::GetDirectoryName(const std::string &path) { + const auto slashPos = FindLastSlash(path); + + if (slashPos == string::npos) { + return ""; + } + + return path.substr(0, slashPos); +} + +string CPath::GetFileNameWithoutExtension(const string &path) { + const auto fileName = GetFileName(path); + + const auto dotPos = fileName.rfind('.'); + + if (dotPos == string::npos) { + return fileName; + } + + return fileName.substr(0, dotPos); +} + +string::size_type CPath::FindLastSlash(const string &path) { + const auto forward = path.rfind('/'); + const auto backward = path.rfind('\\'); + + if (forward == string::npos) { + return backward; + } + + if (backward == string::npos) { + return forward; + } + + return max(forward, backward); +} diff --git a/src/lib/takamori/CPath.h b/src/lib/takamori/CPath.h new file mode 100644 index 0000000..73e5354 --- /dev/null +++ b/src/lib/takamori/CPath.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "../cgss_env.h" + +CGSS_NS_BEGIN + + class CGSS_EXPORT CPath final { + + PURE_STATIC(CPath); + + public: + + static std::string Combine(const std::string &basePath, const std::string &path1); + + static std::string GetExtension(const std::string &path); + + static std::string GetFileName(const std::string &path); + + static std::string GetDirectoryName(const std::string &path); + + static std::string GetFileNameWithoutExtension(const std::string &path); + + private: + + static std::string::size_type FindLastSlash(const std::string &path); + + }; + +CGSS_NS_END diff --git a/src/lib/takamori/streams/CFileStream.cpp b/src/lib/takamori/streams/CFileStream.cpp index 2eadf8f..d9c4676 100644 --- a/src/lib/takamori/streams/CFileStream.cpp +++ b/src/lib/takamori/streams/CFileStream.cpp @@ -1,6 +1,8 @@ #include #include +#include "../CFileSystem.h" + #ifndef __MINGW_H #include @@ -112,15 +114,6 @@ CGSS_NS_BEGIN fflush(_fp); } - bool_t CFileStream::FileExists(LPCSTR fileName) { - auto fp = fopen(fileName, "r"); - if (!fp) { - return FALSE; - } - fclose(fp); - return TRUE; - } - FILE *CFileStream::OpenFile(LPCSTR fileName) { #define __OUT() throw CException("Mode/Access: out of range") #define __CMB() throw CException("Mode/Access: incompatible") @@ -167,7 +160,7 @@ CGSS_NS_BEGIN } break; case FileMode::CreateNew: - if (FileExists(fileName)) { + if (CFileSystem::FileExists(fileName)) { __EXT(); } switch (a) { @@ -181,7 +174,7 @@ CGSS_NS_BEGIN } break; case FileMode::OpenExisting: - if (!FileExists(fileName)) { + if (!CFileSystem::FileExists(fileName)) { __NEX(); } switch (a) { @@ -199,7 +192,7 @@ CGSS_NS_BEGIN } break; case FileMode::OpenOrCreate: - if (!FileExists(fileName)) { + if (!CFileSystem::FileExists(fileName)) { CreateFileInternal(fileName); } switch (a) { @@ -223,7 +216,7 @@ CGSS_NS_BEGIN } void CFileStream::CreateFileInternal(LPCSTR fileName) const { - if (FileExists(fileName)) { + if (CFileSystem::FileExists(fileName)) { return; } auto fp = fopen(fileName, "w"); diff --git a/src/lib/takamori/streams/CFileStream.h b/src/lib/takamori/streams/CFileStream.h index c7fecec..04098cd 100644 --- a/src/lib/takamori/streams/CFileStream.h +++ b/src/lib/takamori/streams/CFileStream.h @@ -39,8 +39,6 @@ CGSS_NS_BEGIN virtual void Flush() override; - static bool_t FileExists(LPCSTR fileName); - protected: CFileStream() = default;