2016-06-21 06:40:13 +00:00
|
|
|
#include "amuse/amuse.hpp"
|
|
|
|
#include "athena/FileReader.hpp"
|
|
|
|
#include "athena/DNAYaml.hpp"
|
|
|
|
#include "logvisor/logvisor.hpp"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2016-06-22 22:15:53 +00:00
|
|
|
static logvisor::Module Log("amuseconv");
|
2016-06-21 06:40:13 +00:00
|
|
|
|
2016-06-22 21:43:45 +00:00
|
|
|
enum ConvType
|
|
|
|
{
|
|
|
|
ConvN64,
|
|
|
|
ConvGCN,
|
|
|
|
ConvPC
|
|
|
|
};
|
|
|
|
|
|
|
|
static void ReportConvType(ConvType tp)
|
|
|
|
{
|
|
|
|
switch (tp)
|
|
|
|
{
|
|
|
|
case ConvN64:
|
|
|
|
Log.report(logvisor::Info, _S("using N64 format"));
|
|
|
|
break;
|
|
|
|
case ConvPC:
|
|
|
|
Log.report(logvisor::Info, _S("using PC format"));
|
|
|
|
break;
|
|
|
|
case ConvGCN:
|
|
|
|
default:
|
|
|
|
Log.report(logvisor::Info, _S("using GameCube format"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-13 06:15:33 +00:00
|
|
|
static bool BuildAudioGroup(amuse::SystemStringView groupBase, amuse::SystemStringView targetPath)
|
2016-06-21 06:40:13 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-13 06:15:33 +00:00
|
|
|
static bool ExtractAudioGroup(amuse::SystemStringView inPath, amuse::SystemStringView targetPath)
|
2016-06-21 06:40:13 +00:00
|
|
|
{
|
|
|
|
amuse::ContainerRegistry::Type type;
|
2017-11-13 06:15:33 +00:00
|
|
|
auto groups = amuse::ContainerRegistry::LoadContainer(inPath.data(), type);
|
2016-06-21 06:40:13 +00:00
|
|
|
|
|
|
|
if (groups.size())
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Info, _S("Found '%s'"), amuse::ContainerRegistry::TypeToName(type));
|
|
|
|
|
2017-11-13 06:15:33 +00:00
|
|
|
amuse::Mkdir(targetPath.data(), 0755);
|
|
|
|
Log.report(logvisor::Info, _S("Established directory at %s"), targetPath.data());
|
2016-06-21 06:40:13 +00:00
|
|
|
|
|
|
|
for (auto& group : groups)
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Info, _S("Extracting %s"), group.first.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-13 06:15:33 +00:00
|
|
|
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.data());
|
2017-11-13 07:21:07 +00:00
|
|
|
amuse::SystemString songsDir = amuse::SystemString(targetPath) + _S("/midifiles");
|
2016-06-21 06:40:13 +00:00
|
|
|
bool madeDir = false;
|
|
|
|
for (auto& pair : songs)
|
|
|
|
{
|
|
|
|
if (!madeDir)
|
|
|
|
{
|
2017-11-13 06:15:33 +00:00
|
|
|
amuse::Mkdir(targetPath.data(), 0755);
|
2016-06-21 06:40:13 +00:00
|
|
|
amuse::Mkdir(songsDir.c_str(), 0755);
|
|
|
|
madeDir = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
amuse::SystemString songPath = songsDir + _S('/') + pair.first + _S(".mid");
|
|
|
|
FILE* fp = amuse::FOpen(songPath.c_str(), _S("wb"));
|
|
|
|
if (fp)
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Info, _S("Extracting %s"), pair.first.c_str());
|
2016-07-02 21:50:38 +00:00
|
|
|
int extractedVersion;
|
|
|
|
bool isBig;
|
2016-07-14 04:54:46 +00:00
|
|
|
std::vector<uint8_t> mid =
|
|
|
|
amuse::SongConverter::SongToMIDI(pair.second.m_data.get(), extractedVersion, isBig);
|
2016-06-21 06:40:13 +00:00
|
|
|
fwrite(mid.data(), 1, mid.size(), fp);
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-13 06:15:33 +00:00
|
|
|
static bool BuildSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath, int version, bool big)
|
2016-06-21 06:40:13 +00:00
|
|
|
{
|
2017-11-13 06:15:33 +00:00
|
|
|
FILE* fp = amuse::FOpen(inPath.data(), _S("rb"));
|
2016-06-22 21:43:45 +00:00
|
|
|
if (!fp)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
|
|
long sz = ftell(fp);
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
std::vector<uint8_t> data(sz, 0);
|
|
|
|
fread(&data[0], 1, sz, fp);
|
|
|
|
fclose(fp);
|
|
|
|
|
2016-07-02 21:50:38 +00:00
|
|
|
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
|
2016-06-22 21:43:45 +00:00
|
|
|
if (out.empty())
|
|
|
|
return false;
|
|
|
|
|
2017-11-13 06:15:33 +00:00
|
|
|
fp = amuse::FOpen(targetPath.data(), _S("wb"));
|
2016-06-22 21:43:45 +00:00
|
|
|
fwrite(out.data(), 1, out.size(), fp);
|
|
|
|
fclose(fp);
|
2016-06-21 06:40:13 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-13 06:15:33 +00:00
|
|
|
static bool ExtractSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath)
|
2016-06-21 06:40:13 +00:00
|
|
|
{
|
2017-11-13 06:15:33 +00:00
|
|
|
FILE* fp = amuse::FOpen(inPath.data(), _S("rb"));
|
2016-06-22 21:43:45 +00:00
|
|
|
if (!fp)
|
|
|
|
return false;
|
2016-06-21 06:40:13 +00:00
|
|
|
|
2016-06-22 21:43:45 +00:00
|
|
|
fseek(fp, 0, SEEK_END);
|
|
|
|
long sz = ftell(fp);
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
std::vector<uint8_t> data(sz, 0);
|
|
|
|
fread(&data[0], 1, sz, fp);
|
|
|
|
fclose(fp);
|
2016-06-21 06:40:13 +00:00
|
|
|
|
2016-07-02 21:50:38 +00:00
|
|
|
int extractedVersion;
|
|
|
|
bool isBig;
|
|
|
|
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), extractedVersion, isBig);
|
2016-06-22 21:43:45 +00:00
|
|
|
if (out.empty())
|
|
|
|
return false;
|
|
|
|
|
2017-11-13 06:15:33 +00:00
|
|
|
fp = amuse::FOpen(targetPath.data(), _S("wb"));
|
2016-06-22 21:43:45 +00:00
|
|
|
fwrite(out.data(), 1, out.size(), fp);
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
return true;
|
2016-06-21 06:40:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if _WIN32
|
|
|
|
int wmain(int argc, const amuse::SystemChar** argv)
|
|
|
|
#else
|
|
|
|
int main(int argc, const amuse::SystemChar** argv)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
logvisor::RegisterConsoleLogger();
|
|
|
|
|
|
|
|
if (argc < 3)
|
|
|
|
{
|
|
|
|
printf("Usage: amuseconv <in-file> <out-file> [n64|pc|gcn]\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConvType type = ConvGCN;
|
|
|
|
if (argc >= 4)
|
|
|
|
{
|
|
|
|
if (!amuse::CompareCaseInsensitive(argv[3], _S("n64")))
|
|
|
|
type = ConvN64;
|
|
|
|
else if (!amuse::CompareCaseInsensitive(argv[3], _S("gcn")))
|
|
|
|
type = ConvGCN;
|
|
|
|
else if (!amuse::CompareCaseInsensitive(argv[3], _S("pc")))
|
|
|
|
type = ConvPC;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, _S("unrecognized format: %s"), argv[3]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool good = false;
|
|
|
|
FILE* fin = amuse::FOpen(argv[1], _S("rb"));
|
|
|
|
if (fin)
|
|
|
|
{
|
|
|
|
fclose(fin);
|
|
|
|
amuse::SystemString barePath(argv[1]);
|
|
|
|
size_t dotPos = barePath.rfind(_S('.'));
|
|
|
|
const amuse::SystemChar* dot = barePath.c_str() + dotPos;
|
|
|
|
if (dotPos != amuse::SystemString::npos)
|
|
|
|
{
|
2016-07-14 04:54:46 +00:00
|
|
|
if (!amuse::CompareCaseInsensitive(dot, _S(".mid")) || !amuse::CompareCaseInsensitive(dot, _S(".midi")))
|
2016-06-21 06:40:13 +00:00
|
|
|
{
|
|
|
|
ReportConvType(type);
|
2016-07-02 21:50:38 +00:00
|
|
|
good = BuildSNG(barePath, argv[2], 1, true);
|
2016-06-21 06:40:13 +00:00
|
|
|
}
|
2016-07-14 04:54:46 +00:00
|
|
|
else if (!amuse::CompareCaseInsensitive(dot, _S(".son")) || !amuse::CompareCaseInsensitive(dot, _S(".sng")))
|
2016-06-21 06:40:13 +00:00
|
|
|
{
|
|
|
|
good = ExtractSNG(argv[1], argv[2]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
good = ExtractAudioGroup(argv[1], argv[2]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
amuse::Sstat theStat;
|
|
|
|
if (!amuse::Stat(argv[1], &theStat) && S_ISDIR(theStat.st_mode))
|
|
|
|
{
|
|
|
|
amuse::SystemString projectPath(argv[1]);
|
|
|
|
projectPath += _S("/project.yaml");
|
|
|
|
fin = amuse::FOpen(projectPath.c_str(), _S("rb"));
|
|
|
|
if (fin)
|
|
|
|
{
|
|
|
|
fclose(fin);
|
|
|
|
ReportConvType(type);
|
|
|
|
good = BuildAudioGroup(argv[1], argv[2]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!good)
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, _S("unable to convert %s to %s"), argv[1], argv[2]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|