Adding modconv tool.

This commit is contained in:
Nicolas Pixel Noble 2024-02-18 13:18:42 -08:00
parent bed8be6c2c
commit 06bfd2d3f8
12 changed files with 560 additions and 28 deletions

View file

@ -169,7 +169,7 @@ SUPPORT_SRCS += third_party/ucl/src/n2e_99.c third_party/ucl/src/alloc.c
SUPPORT_SRCS += $(wildcard third_party/iec-60908b/*.c)
OBJECTS := third_party/luajit/src/libluajit.a
TOOLS = exe2elf exe2iso ps1-packer psyq-obj-parser
TOOLS = exe2elf exe2iso modconv ps1-packer psyq-obj-parser
##############################################################################

View file

@ -64,8 +64,9 @@ steps:
vsprojects/x64/ReleaseCLI/crashpad_handler.exe
vsprojects/x64/ReleaseCLI/exe2elf.exe
vsprojects/x64/ReleaseCLI/exe2iso.exe
vsprojects/x64/ReleaseCLI/psyq-obj-parser.exe
vsprojects/x64/ReleaseCLI/modconv.exe
vsprojects/x64/ReleaseCLI/ps1-packer.exe
vsprojects/x64/ReleaseCLI/psyq-obj-parser.exe
vsprojects/x64/ReleaseCLI/*.dll
TargetFolder: '$(build.artifactStagingDirectory)/binaries'
@ -83,11 +84,12 @@ steps:
!**\crashpad_handler.exe
!**\exe2elf.exe
!**\exe2iso.exe
!**\pcsxrunner.exe
!**\pcsx-wrapper.exe
!**\modconv.exe
!**\pcsx-redux.exe
!**\psyq-obj-parser.exe
!**\pcsx-wrapper.exe
!**\pcsxrunner.exe
!**\ps1-packer.exe
!**\psyq-obj-parser.exe
!third_party\**\*.exe
searchFolder: '$(System.DefaultWorkingDirectory)'
pathtoCustomTestAdapters: 'GoogleTestAdapter'

View file

@ -72,8 +72,9 @@ steps:
vsprojects/x64/ReleaseWithClangCL/crashpad_handler.exe
vsprojects/x64/ReleaseWithClangCL/exe2elf.exe
vsprojects/x64/ReleaseWithClangCL/exe2iso.exe
vsprojects/x64/ReleaseWithClangCL/psyq-obj-parser.exe
vsprojects/x64/ReleaseWithClangCL/modconv.exe
vsprojects/x64/ReleaseWithClangCL/ps1-packer.exe
vsprojects/x64/ReleaseWithClangCL/psyq-obj-parser.exe
vsprojects/x64/ReleaseWithClangCL/*.dll
TargetFolder: '$(build.artifactStagingDirectory)/binaries'
@ -91,11 +92,12 @@ steps:
!**\crashpad_handler.exe
!**\exe2elf.exe
!**\exe2iso.exe
!**\pcsxrunner.exe
!**\modconv.exe
!**\pcsx-redux.main
!**\pcsx-redux.exe
!**\psyq-obj-parser.exe
!**\pcsxrunner.exe
!**\ps1-packer.exe
!**\psyq-obj-parser.exe
!third_party\**\*.exe
searchFolder: '$(System.DefaultWorkingDirectory)'
pathtoCustomTestAdapters: 'GoogleTestAdapter'

View file

@ -5,3 +5,5 @@ This code is a reverse engineering of the file MODPLAY.BIN, located in the zip f
The current API behaves roughly the same the original one. The code should provide information about the alterations made to it.
The demo song, "timewarped", written by [Jesster](https://modarchive.org/index.php?request=view_profile&query=69138), comes from [the modarchive](https://modarchive.org/index.php?request=view_by_moduleid&query=106481) with a [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/) license, allowing non-commercial adaptations. The file has been converted from MOD to HIT using the MODCONV.EXE software provided by Hitmen.
A modern recreation of the MODCONV.EXE software is available within the PCSX-Redux project itself. See the [modconv](https://github.com/grumpycoders/pcsx-redux/tree/main/tools/modconv) folder for more information.

View file

@ -218,7 +218,7 @@ static void MOD_SetBPM(unsigned bpm) {
static struct SPUChannelData s_channelData[24];
uint32_t MOD_Load(const struct MODFileFormat* module) {
static uint32_t loadInternal(const struct MODFileFormat* module, const uint8_t* sampleData) {
SPUInit();
MOD_Channels = MOD_Check(module);
@ -241,8 +241,12 @@ uint32_t MOD_Load(const struct MODFileFormat* module) {
MOD_ModuleData = (const uint8_t*)&module->patternTable[0];
SPUUploadInstruments(0x1010, MOD_ModuleData + 4 + 128 + MOD_Channels * 0x100 * (maxPatternID + 1),
currentSpuAddress - 0x1010);
if (sampleData) {
SPUUploadInstruments(0x1010, sampleData, currentSpuAddress - 0x1010);
} else {
SPUUploadInstruments(0x1010, MOD_ModuleData + 4 + 128 + MOD_Channels * 0x100 * (maxPatternID + 1),
currentSpuAddress - 0x1010);
}
MOD_CurrentOrder = 0;
MOD_CurrentPattern = module->patternTable[0];
@ -274,6 +278,13 @@ uint32_t MOD_Load(const struct MODFileFormat* module) {
return 4 + 128 + MOD_Channels * 0x100 * (maxPatternID + 1);
}
uint32_t MOD_Load(const struct MODFileFormat* module) { return loadInternal(module, NULL); }
unsigned MOD_LoadEx(const struct MODFileFormat* module, const uint8_t* sampleData) {
loadInternal(module, sampleData);
return MOD_Channels;
}
void MOD_Silence() {
SPUInit();
for (unsigned i = 0; i < 24; i++) {

View file

@ -76,6 +76,17 @@ unsigned MOD_Check(const struct MODFileFormat* module);
// the SPU.
uint32_t MOD_Load(const struct MODFileFormat* module);
// Loads the specified module and gets it ready for
// playback. The pointers have to be aligned to a
// 4-bytes boundary. Will also setup the SPU. This
// call is meant to be used with the separate .smp
// file, which the new modconv.exe tool can generate.
// Returns the number of channels from the module,
// or 0 if the module is invalid. No relocation is
// needed, and the sampleData pointer can simply be
// freed after this call.
unsigned MOD_LoadEx(const struct MODFileFormat* module, const uint8_t* sampleData);
// Call this function periodically to play sound. The
// frequency at which this is called will determine the
// actual playback speed of the module. Most modules will

View file

@ -29,6 +29,7 @@ SOFTWARE.
#include <stdint.h>
#include <bit>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
@ -91,10 +92,6 @@ struct UInt64 : public BasicFieldType<uint64_t, std::endian::little> {
static constexpr char const typeName[] = "uint64_t";
};
struct BEInt8 : public BasicFieldType<int8_t, std::endian::big> {
static constexpr char const typeName[] = "int8_t";
};
struct BEInt16 : public BasicFieldType<int16_t, std::endian::big> {
static constexpr char const typeName[] = "int16_t";
};
@ -107,10 +104,6 @@ struct BEInt64 : public BasicFieldType<int64_t, std::endian::big> {
static constexpr char const typeName[] = "int64_t";
};
struct BEUInt8 : public BasicFieldType<uint8_t, std::endian::big> {
static constexpr char const typeName[] = "uint8_t";
};
struct BEUInt16 : public BasicFieldType<uint16_t, std::endian::big> {
static constexpr char const typeName[] = "uint16_t";
};
@ -160,11 +153,17 @@ struct CString {
memcpy(value, v.data(), S);
return *this;
}
void set(const type &v) { memcpy(value, v.data(), S); }
void set(const type &v) {
value[S] = 0;
memcpy(value, v.data(), S);
}
void serialize(IO<File> f) const { f->write(value, S); }
void deserialize(IO<File> f) { f->read(value, S); }
void reset() { memset(value, 0, S); }
char value[S];
void deserialize(IO<File> f) {
value[S] = 0;
f->read(value, S);
}
void reset() { memset(value, 0, S + 1); }
char value[S + 1];
};
template <typename FieldType, typename name>
@ -183,6 +182,70 @@ struct StructField<StructType, irqus::typestring<C...>> : public StructType {
typedef irqus::typestring<C...> fieldName;
};
template <typename FieldType, typename name, size_t N>
struct RepeatedField;
template <typename FieldType, char... C, size_t N>
struct RepeatedField<FieldType, irqus::typestring<C...>, N> {
RepeatedField() {}
typedef irqus::typestring<C...> fieldName;
FieldType value[N];
FieldType &operator[](size_t i) {
if (i >= N) throw std::out_of_range("Index out of range");
return value[i];
}
const FieldType &operator[](size_t i) const {
if (i >= N) throw std::out_of_range("Index out of range");
return value[i];
}
void serialize(IO<File> f) const {
for (size_t i = 0; i < N; i++) {
value[i].serialize(f);
}
}
void deserialize(IO<File> f) {
for (size_t i = 0; i < N; i++) {
value[i].deserialize(f);
}
}
void reset() {
for (size_t i = 0; i < N; i++) {
value[i].reset();
}
}
};
template <typename FieldType, typename name, size_t N>
struct RepeatedStruct;
template <typename FieldType, char... C, size_t N>
struct RepeatedStruct<FieldType, irqus::typestring<C...>, N> {
RepeatedStruct() {}
typedef irqus::typestring<C...> fieldName;
FieldType value[N];
FieldType &operator[](size_t i) {
if (i >= N) throw std::out_of_range("Index out of range");
return value[i];
}
const FieldType &operator[](size_t i) const {
if (i >= N) throw std::out_of_range("Index out of range");
return value[i];
}
void serialize(IO<File> f) const {
for (size_t i = 0; i < N; i++) {
value[i].serialize(f);
}
}
void deserialize(IO<File> f) {
for (size_t i = 0; i < N; i++) {
value[i].deserialize(f);
}
}
void reset() {
for (size_t i = 0; i < N; i++) {
value[i].reset();
}
}
};
template <typename name, typename... fields>
class Struct;
template <char... C, typename... fields>
@ -208,8 +271,8 @@ class Struct<irqus::typestring<C...>, fields...> : private std::tuple<fields...>
constexpr void reset() {}
template <size_t index, typename FieldType, typename... nestedFields>
constexpr void reset() {
FieldType &setting = std::get<index>(*this);
setting.reset();
FieldType &field = std::get<index>(*this);
field.reset();
reset<index + 1, nestedFields...>();
}
template <size_t index>

22
tools/modconv/README.md Normal file
View file

@ -0,0 +1,22 @@
# MODCONV
This folder contains a modern recreation of the MODCONV.EXE software, which is used to convert MOD files to HIT files. The original software was [provided by Hitmen](http://hitmen.c02.at/html/psx_tools.html), without source code.
This version has been written from scratch, without reverse engineering, as the file format is fairly simple to understand. This means that the output files will be slightly different from the original software.
Its purpose is to convert [MOD files](https://en.wikipedia.org/wiki/Module_file) to HIT files, which can then be played by the [modplayer library available](https://github.com/grumpycoders/pcsx-redux/tree/main/src/mips/modplayer) in the PCSX-Redux project.
## Usage
```sh
modconv input.mod [-s output.smp] [-a amp] -o output.hit
```
## Arguments
| Argument | Type | Description |
|-|-|-|
| input.mod | mandatory | Specify the input mod file. |
| -o output.hit | mandatory | Name of the output file. |
| -s output.smp | optional | Name of the output samples data file. |
| -a amp | optional | Amplification factor. Default is 175 |
| -h | optional | Show help. |
If the `-i` argument is not provided, the sample data will be written to the .hit file itself, and can be loaded with the `MOD_Load` function from the modplayer library or the old Hitmen implementation. If the `-i` argument is provided, the sample data will be written into a separate file. Both will need to be loaded with the `MOD_LoadEx` function from the modplayer library, and is not backwards compatible with the old Hitmen implementation. This allows the user to have a simple way to unload the samples data from the main ram after the call to `MOD_LoadEx`, only keeping the .hit file in memory.

260
tools/modconv/modconv.cc Normal file
View file

@ -0,0 +1,260 @@
/***************************************************************************
* Copyright (C) 2024 PCSX-Redux authors *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
#include <algorithm>
#include <cctype>
#include <memory>
#include <string_view>
#include "flags.h"
#include "fmt/format.h"
#include "support/binstruct.h"
#include "support/file.h"
#include "support/typestring-wrapper.h"
#include "supportpsx/adpcm.h"
typedef PCSX::BinStruct::Field<PCSX::BinStruct::CString<20>, TYPESTRING("Title")> ModTitle;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::CString<22>, TYPESTRING("Name")> SampleName;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::BEUInt16, TYPESTRING("Length")> SampleLength;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::UInt8, TYPESTRING("FineTune")> SampleFineTune;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::UInt8, TYPESTRING("Volume")> SampleVolume;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::BEUInt16, TYPESTRING("LoopStart")> SampleLoopStart;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::BEUInt16, TYPESTRING("LoopLength")> SampleLoopLength;
typedef PCSX::BinStruct::Struct<TYPESTRING("ModSample"), SampleName, SampleLength, SampleFineTune, SampleVolume,
SampleLoopStart, SampleLoopLength>
ModSample;
typedef PCSX::BinStruct::RepeatedStruct<ModSample, TYPESTRING("ModSamples"), 31> ModSamples;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::UInt8, TYPESTRING("Positions")> Positions;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::UInt8, TYPESTRING("RestartPosition")> RestartPosition;
typedef PCSX::BinStruct::RepeatedField<PCSX::BinStruct::UInt8, TYPESTRING("PatternTable"), 128> PatternTable;
typedef PCSX::BinStruct::Field<PCSX::BinStruct::CString<4>, TYPESTRING("Signature")> Signature;
typedef PCSX::BinStruct::Struct<TYPESTRING("ModFile"), ModTitle, ModSamples, Positions, RestartPosition, PatternTable,
Signature>
ModFile;
int main(int argc, char** argv) {
CommandLine::args args(argc, argv);
auto output = args.get<std::string>("o");
fmt::print(R"(
modconv by Nicolas "Pixel" Noble
https://github.com/grumpycoders/pcsx-redux/tree/main/tools/modconv/
)");
auto inputs = args.positional();
const bool asksForHelp = args.get<bool>("h").value_or(false);
const bool hasOutput = output.has_value();
const bool oneInput = inputs.size() == 1;
auto samplesFile = args.get<std::string>("s");
auto amplification = args.get<unsigned>("a").value_or(175);
if (asksForHelp || !oneInput || !hasOutput) {
fmt::print(R"(
Usage: {} input.mod [-h] [-s output.ins] [-a amp] -o output.hit
input.mod mandatory: specify the input mod file
-o output.hit mandatory: name of the output hit file.
-h displays this help information and exit.
-s output.smp optional: name of the output sample file.
-a amplification optional: value of sample amplification. Defaults to 175.
If the -s option is specified, the .hit file will only contain the pattern data,
and the .smp file will contain the sample data which can be loaded into the SPU
memory separately. If the -s option is not specified, the .hit file will contain
both the pattern and sample data.
)",
argv[0]);
return -1;
}
auto& input = inputs[0];
PCSX::IO<PCSX::File> file(new PCSX::PosixFile(input));
if (file->failed()) {
fmt::print("Unable to open file: {}\n", input);
return -1;
}
ModFile modFile;
modFile.deserialize(file);
std::string_view signature(modFile.get<Signature>().value, 4);
unsigned channels = 0;
if (signature == "M.K." || signature == "M!K!") {
channels = 4;
} else if (std::isdigit(signature[0]) && (signature[1] == 'C') && (signature[2] == 'H') && (signature[3] == 'N')) {
channels = signature[0] - '0';
} else if (std::isdigit(signature[0]) && std::isdigit(signature[1]) && (signature[2] == 'C') &&
(signature[3] == 'H')) {
channels = (signature[0] - '0') * 10 + signature[1] - '0';
}
if (channels == 0) {
fmt::print("{} doesn't have a recognized MOD file format.\n", input);
return -1;
}
if (channels > 24) {
fmt::print("{} has too many channels ({}). The maximum is 24.\n", input, channels);
return -1;
}
unsigned maxPatternID = 0;
for (unsigned i = 0; i < 128; i++) {
maxPatternID = std::max(maxPatternID, unsigned(modFile.get<PatternTable>()[i]));
}
auto patternData = file->read(channels * (maxPatternID + 1) * 256);
fmt::print("Title: {}\n", modFile.get<ModTitle>().value);
fmt::print("Positions: {}\n", modFile.get<Positions>().value);
fmt::print("Patterns: {}\n", maxPatternID + 1);
fmt::print("Converting samples...\n");
PCSX::IO<PCSX::File> encodedSamples =
samplesFile.has_value()
? reinterpret_cast<PCSX::File*>(new PCSX::PosixFile(samplesFile.value().c_str(), PCSX::FileOps::TRUNCATE))
: reinterpret_cast<PCSX::File*>(new PCSX::BufferFile(PCSX::FileOps::READWRITE));
constexpr uint8_t silentLoopBlock[16] = {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
std::unique_ptr<PCSX::ADPCM::Encoder> encoder(new PCSX::ADPCM::Encoder);
for (unsigned i = 0; i < 31; i++) {
encoder->reset();
auto& sample = modFile.get<ModSamples>()[i];
fmt::print("Sample {:2} [{:22}] - ", i + 1, sample.get<SampleName>().value);
auto length = sample.get<SampleLength>().value;
auto loopStart = sample.get<SampleLoopStart>().value;
auto loopLength = sample.get<SampleLoopLength>().value;
bool hasLoop = (loopStart > 0) && (loopLength > 1);
if (length == 0) {
fmt::print("Empty\n");
continue;
}
int16_t input[28];
uint8_t spuBlock[16];
file->skip<uint16_t>();
length--;
length *= 2;
unsigned position = 2;
loopStart *= 2;
unsigned loopEnd = loopStart + loopLength * 2;
unsigned encodedLength = 0;
while (length >= 28) {
for (unsigned j = 0; j < 28; j++) {
input[j] = int16_t(file->read<int8_t>()) * amplification;
}
length -= 28;
encoder->processSPUBlock(input, spuBlock, PCSX::ADPCM::Encoder::BlockAttribute::OneShot);
uint8_t blockAttribute = 0;
if (length == 0) {
blockAttribute |= 1;
}
if (hasLoop && (loopStart <= position)) {
blockAttribute |= 2;
if (position < (loopStart + 28)) {
blockAttribute |= 4;
}
}
spuBlock[1] = blockAttribute;
position += 28;
encodedSamples->write(spuBlock, 16);
encodedLength += 16;
}
if (length != 0) {
for (unsigned j = 0; j < length; j++) {
input[j] = int16_t(file->read<int8_t>()) * amplification;
}
for (unsigned j = length; j < 28; j++) {
input[j] = 0;
}
encoder->processSPUBlock(input, spuBlock, PCSX::ADPCM::Encoder::BlockAttribute::OneShot);
uint8_t blockAttribute = 0;
if (hasLoop) {
blockAttribute = 3;
if (position < (loopStart + 28)) {
blockAttribute |= 4;
}
}
spuBlock[1] = blockAttribute;
position += 28;
encodedSamples->write(spuBlock, 16);
encodedLength += 16;
}
if (!hasLoop) {
encodedSamples->write(silentLoopBlock, 16);
encodedLength += 16;
}
fmt::print("Size {} -> {}\n", sample.get<SampleLength>().value * 2 - 2, encodedLength);
sample.get<SampleLength>().value = encodedLength;
if (encodedLength >= 65536) {
fmt::print("Sample too big.\n");
return -1;
}
}
if (channels >= 10) {
modFile.get<Signature>().value[0] = 'H';
modFile.get<Signature>().value[1] = 'M';
modFile.get<Signature>().value[2] = (channels / 10) + '0';
modFile.get<Signature>().value[3] = (channels % 10) + '0';
} else {
modFile.get<Signature>().value[0] = 'H';
modFile.get<Signature>().value[1] = 'I';
modFile.get<Signature>().value[2] = 'T';
modFile.get<Signature>().value[3] = channels + '0';
}
unsigned fullLength = 0;
for (unsigned i = 0; i < 31; i++) {
auto& sample = modFile.get<ModSamples>()[i];
fullLength += sample.get<SampleLength>().value;
}
constexpr unsigned spuMemory = 512 * 1024 - 0x1010;
if (fullLength >= spuMemory) {
fmt::print("Not enough SPU memory to store all samples; {} bytes required but only {} available.\n", fullLength,
spuMemory);
return -1;
} else {
fmt::print("Used {} bytes of SPU memory, {} still available.\n", fullLength, spuMemory - fullLength);
}
PCSX::IO<PCSX::File> out(new PCSX::PosixFile(output.value().c_str(), PCSX::FileOps::TRUNCATE));
modFile.serialize(out);
out->write(std::move(patternData));
if (!samplesFile.has_value()) {
out->write(std::move(encodedSamples.asA<PCSX::BufferFile>()->borrow()));
}
out->close();
encodedSamples->close();
if (samplesFile.has_value()) {
fmt::print("All done, files {} and {} written out.\n", output.value(), args.get<std::string>("s").value());
} else {
fmt::print("All done, file {} written out.\n", output.value());
}
return 0;
}

View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="ReleaseWithClangCL|x64">
<Configuration>ReleaseWithClangCL</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{74f6a549-ab14-4369-a382-c31c0ed97a92}</ProjectGuid>
<RootNamespace>modconv</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithClangCL|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\common.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\common.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithClangCL|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\common.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithClangCL|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\fmt\fmt.vcxproj">
<Project>{71772007-5110-418d-be9c-fb102b6eaabf}</Project>
</ProjectReference>
<ProjectReference Include="..\supportpsx\supportpsx.vcxproj">
<Project>{b2e2ad84-9d7f-4976-9572-e415819ffd7f}</Project>
</ProjectReference>
<ProjectReference Include="..\support\support.vcxproj">
<Project>{0e621321-093c-4d60-bd8b-027fdc2b0f63}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\tools\modconv\modconv.cc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\tools\modconv\modconv.cc">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -103,6 +103,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lua", "Lua", "{394627A0-57E
..\tests\lua\file.lua = ..\tests\lua\file.lua
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "modconv", "modconv\modconv.vcxproj", "{74F6A549-AB14-4369-A382-C31C0ED97A92}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -466,8 +468,8 @@ Global
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.Debug|x64.Build.0 = Debug|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.Release|x64.ActiveCfg = Release|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.Release|x64.Build.0 = Release|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.ReleaseCLI|x64.ActiveCfg = Release|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.ReleaseCLI|x64.Build.0 = Release|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.ReleaseCLI|x64.ActiveCfg = ReleaseWithClangCL|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.ReleaseCLI|x64.Build.0 = ReleaseWithClangCL|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.ReleaseWithClangCL|x64.ActiveCfg = ReleaseWithClangCL|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.ReleaseWithClangCL|x64.Build.0 = ReleaseWithClangCL|x64
{CDED480F-14EE-475E-97F5-97F2B62DB3CE}.ReleaseWithTracy|x64.ActiveCfg = Release|x64
@ -492,6 +494,16 @@ Global
{CE54ED92-4645-4AE9-BDC8-C0B9607765F8}.ReleaseWithClangCL|x64.Build.0 = ReleaseWithClangCL|x64
{CE54ED92-4645-4AE9-BDC8-C0B9607765F8}.ReleaseWithTracy|x64.ActiveCfg = ReleaseWithTracy|x64
{CE54ED92-4645-4AE9-BDC8-C0B9607765F8}.ReleaseWithTracy|x64.Build.0 = ReleaseWithTracy|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.Debug|x64.ActiveCfg = Debug|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.Debug|x64.Build.0 = Debug|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.Release|x64.ActiveCfg = Release|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.Release|x64.Build.0 = Release|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.ReleaseCLI|x64.ActiveCfg = ReleaseWithClangCL|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.ReleaseCLI|x64.Build.0 = ReleaseWithClangCL|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.ReleaseWithClangCL|x64.ActiveCfg = ReleaseWithClangCL|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.ReleaseWithClangCL|x64.Build.0 = ReleaseWithClangCL|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.ReleaseWithTracy|x64.ActiveCfg = Release|x64
{74F6A549-AB14-4369-A382-C31C0ED97A92}.ReleaseWithTracy|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -537,9 +549,10 @@ Global
{B2E2AD84-9D7F-4976-9572-E415819FFD7F} = {008A2872-432F-480B-828D-FF9AAA4846BC}
{CE54ED92-4645-4AE9-BDC8-C0B9607765F8} = {64A05F50-3203-42CC-B632-09D6EE6EA856}
{394627A0-57EB-46B1-B768-E02ACFC798A8} = {9D5A1DB2-E74D-4CDD-8377-9EA08CF4AADE}
{74F6A549-AB14-4369-A382-C31C0ED97A92} = {C6DD47BC-0C38-4AE6-B517-9675F3AC8A50}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {284E91C1-764E-441A-854D-CFD3623A6501}
SolutionGuid = {AC54A867-F976-4B3D-A6EF-F57EB764DCD4}
SolutionGuid = {284E91C1-764E-441A-854D-CFD3623A6501}
EndGlobalSection
EndGlobal