Skip to content

Commit

Permalink
Added acb2wavs, and various fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
hozuki committed Oct 17, 2018
1 parent 0b8ad79 commit 3921d67
Show file tree
Hide file tree
Showing 29 changed files with 842 additions and 137 deletions.
1 change: 1 addition & 0 deletions cmake/project_compile.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions cmake/project_files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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})
1 change: 1 addition & 0 deletions cmake/project_link.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
4 changes: 4 additions & 0 deletions cmake/project_postbuild.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ if (${GNU_COMPILER})
POST_BUILD
COMMAND ${CMAKE_STRIP} -s $<TARGET_FILE:acbunpack>
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
add_custom_command(TARGET acb2wavs
POST_BUILD
COMMAND ${CMAKE_STRIP} -s $<TARGET_FILE:acb2wavs>
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
add_custom_command(TARGET cgss
POST_BUILD
COMMAND ${CMAKE_STRIP} -s $<TARGET_FILE:cgss>
Expand Down
1 change: 1 addition & 0 deletions cmake/project_targets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
286 changes: 286 additions & 0 deletions src/apps/acb2wavs/acb2wavs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
#include "../cgssh.h"
#include "../../lib/cgss_api.h"

#include <iostream>
#include <string>
#include <algorithm>

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<typename T>
T atoh(const char *str);

template<typename T>
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 <acb file> [-a <key1> -b <key2>]" << 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<uint32_t>(argv[++i]);
}
break;
case 'b':
if (i + 1 < argc) {
cc.keyParts.key2 = atoh<uint32_t>(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<typename T>
T atoh(const char *str) {
return atoh<T>(str, 8);
}

template<typename T>
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;
}
2 changes: 1 addition & 1 deletion src/lib/capi/cgss_capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/cgss_intf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading

0 comments on commit 3921d67

Please sign in to comment.