Merge branch 'ToolBox_TODOs_SupportSFX' into ToolBox_TODOs
This commit is contained in:
commit
d258d79f90
|
@ -39,6 +39,8 @@ struct DefaultConfiguration {
|
||||||
```
|
```
|
||||||
### `CustomConfiguration` macros
|
### `CustomConfiguration` macros
|
||||||
```c++
|
```c++
|
||||||
|
// Turns on support of Sonys VAG files for the CDFileProcessor (off by default)
|
||||||
|
#define __SUPPORT_VAG__
|
||||||
// Turns on colored rectangles during boot (off by default)
|
// Turns on colored rectangles during boot (off by default)
|
||||||
#define __USE_DEBUG_COLOR__
|
#define __USE_DEBUG_COLOR__
|
||||||
// Turns on PS3 support (on by default)
|
// Turns on PS3 support (on by default)
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace JabyEngine {
|
||||||
State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload) {
|
State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload) {
|
||||||
switch(static_cast<FileType>(file_type)) {
|
switch(static_cast<FileType>(file_type)) {
|
||||||
case FileType::Jingle:
|
case FileType::Jingle:
|
||||||
return State::from(JingleState{.sfx_id = payload.raw}, reinterpret_cast<const uint8_t*>(data_adr), parse_jingle);
|
return State::from(JingleState{.sfx_id = payload.raw}, data_adr, parse_jingle);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FileProcessor::create(data_adr, Nothing());
|
return FileProcessor::create(data_adr, Nothing());
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "include/controller_state.hpp"
|
#include "include/controller_state.hpp"
|
||||||
#include <PSX/GPU/gpu.hpp>
|
#include <PSX/GPU/gpu.hpp>
|
||||||
|
#include <PSX/SPU/spu.hpp>
|
||||||
|
|
||||||
namespace ControllerTest {
|
namespace ControllerTest {
|
||||||
using DigitalButton = Periphery::AnalogeController::Button;
|
using DigitalButton = Periphery::AnalogeController::Button;
|
||||||
|
@ -7,6 +8,9 @@ namespace ControllerTest {
|
||||||
|
|
||||||
static void set_active(GPU::SPRT_16::Linked& sprt, bool is_active) {
|
static void set_active(GPU::SPRT_16::Linked& sprt, bool is_active) {
|
||||||
sprt->tex_offset.y = is_active ? 16 : 0;
|
sprt->tex_offset.y = is_active ? 16 : 0;
|
||||||
|
if(is_active) {
|
||||||
|
SPU::voice[1].play_if_end();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_active(GPU::POLY_FT4::Linked& poly, bool is_active) {
|
static void set_active(GPU::POLY_FT4::Linked& poly, bool is_active) {
|
||||||
|
@ -14,6 +18,10 @@ namespace ControllerTest {
|
||||||
poly->tex_offset1.y = is_active ? 16 : 0;
|
poly->tex_offset1.y = is_active ? 16 : 0;
|
||||||
poly->tex_offset2.y = is_active ? 32 : 16;
|
poly->tex_offset2.y = is_active ? 32 : 16;
|
||||||
poly->tex_offset3.y = is_active ? 32 : 16;
|
poly->tex_offset3.y = is_active ? 32 : 16;
|
||||||
|
|
||||||
|
if(is_active) {
|
||||||
|
SPU::voice[1].play_if_end();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* get_type_name(Periphery::ControllerType type) {
|
static const char* get_type_name(Periphery::ControllerType type) {
|
||||||
|
|
|
@ -17,6 +17,8 @@ namespace Assets {
|
||||||
__jabyengine_start_lba_request
|
__jabyengine_start_lba_request
|
||||||
__jabyengine_request_lba_for(PACO, "ASSETS/MAIN/PACO.IMG"),
|
__jabyengine_request_lba_for(PACO, "ASSETS/MAIN/PACO.IMG"),
|
||||||
__jabyengine_request_lba_for(DFISH, "ASSETS/MAIN/DFISH.IMG"),
|
__jabyengine_request_lba_for(DFISH, "ASSETS/MAIN/DFISH.IMG"),
|
||||||
|
__jabyengine_request_lba_for(APPLE_SFX, "SFX/APPLE.VAG"),
|
||||||
|
__jabyengine_request_lba_for(BLUBB_SFX, "SFX/BLUBB.VAG"),
|
||||||
__jabyengine_request_lba_for(MIX_XA, "XAAUDIO/MIX.XA"),
|
__jabyengine_request_lba_for(MIX_XA, "XAAUDIO/MIX.XA"),
|
||||||
__jabyengine_request_lba_for(BIOS_INFO_OVL, "BIO.BIN"),
|
__jabyengine_request_lba_for(BIOS_INFO_OVL, "BIO.BIN"),
|
||||||
__jabyengine_request_lba_for(GPU_TEST_OVL, "GTO.BIN"),
|
__jabyengine_request_lba_for(GPU_TEST_OVL, "GTO.BIN"),
|
||||||
|
@ -58,9 +60,13 @@ namespace Assets {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
|
using SPU::operator""_vol;
|
||||||
|
|
||||||
static const CDFile Files[] = {
|
static const CDFile Files[] = {
|
||||||
CDFileBuilder::simple_tim(LBA::PACO, PacoTIM),
|
CDFileBuilder::simple_tim(LBA::PACO, PacoTIM),
|
||||||
CDFileBuilder::simple_tim(LBA::DFISH, DoenerFishInfo.tim),
|
CDFileBuilder::simple_tim(LBA::DFISH, DoenerFishInfo.tim),
|
||||||
|
CDFileBuilder::sony_vag(LBA::APPLE_SFX, VAG::create(0, 1.0_vol)),
|
||||||
|
CDFileBuilder::sony_vag(LBA::BLUBB_SFX, VAG::create(1, 1.0_vol)),
|
||||||
CustomCDFileBuilder::jingle(32),
|
CustomCDFileBuilder::jingle(32),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "../include/shared.hpp"
|
#include "../include/shared.hpp"
|
||||||
#include "include/menu.hpp"
|
#include "include/menu.hpp"
|
||||||
#include <PSX/Periphery/periphery.hpp>
|
#include <PSX/Periphery/periphery.hpp>
|
||||||
|
#include <PSX/SPU/spu.hpp>
|
||||||
|
|
||||||
namespace Menu {
|
namespace Menu {
|
||||||
using DigitalButton = Periphery::GenericController::Button;
|
using DigitalButton = Periphery::GenericController::Button;
|
||||||
|
@ -24,6 +25,7 @@ namespace Menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(controller.button.went_down(DigitalButton::Cross)) {
|
if(controller.button.went_down(DigitalButton::Cross)) {
|
||||||
|
SPU::voice[0].play();
|
||||||
this->selection_callback(this->cur_selection);
|
this->selection_callback(this->cur_selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ CLUT_4_COLOR_TRANS_FLAGS = simple-tim clut4 --color-trans
|
||||||
## Music tracks
|
## Music tracks
|
||||||
INPUT += $(OUTPUT_DIR)/Evacuation_cdda.xa
|
INPUT += $(OUTPUT_DIR)/Evacuation_cdda.xa
|
||||||
INPUT += $(OUTPUT_DIR)/fox.xa
|
INPUT += $(OUTPUT_DIR)/fox.xa
|
||||||
|
INPUT += $(OUTPUT_DIR)/apple.vag
|
||||||
|
INPUT += $(OUTPUT_DIR)/blubb.vag
|
||||||
|
|
||||||
## Images
|
## Images
|
||||||
INPUT += $(OUTPUT_DIR)/TexturePage.img
|
INPUT += $(OUTPUT_DIR)/TexturePage.img
|
||||||
|
@ -36,6 +38,10 @@ $(OUTPUT_DIR)/fox.xa: audio/temp/fox.wav
|
||||||
@mkdir -p $(OUTPUT_DIR)
|
@mkdir -p $(OUTPUT_DIR)
|
||||||
jaby_engine_fconv $< -o $@ xa
|
jaby_engine_fconv $< -o $@ xa
|
||||||
|
|
||||||
|
$(OUTPUT_DIR)/%.vag: audio/temp/%.wav
|
||||||
|
@mkdir -p $(OUTPUT_DIR)
|
||||||
|
jaby_engine_fconv $< -o $@ vag
|
||||||
|
|
||||||
$(OUTPUT_DIR)/%.xa: audio/%.wav
|
$(OUTPUT_DIR)/%.xa: audio/%.wav
|
||||||
@mkdir -p $(OUTPUT_DIR)
|
@mkdir -p $(OUTPUT_DIR)
|
||||||
jaby_engine_fconv $< -o $@ xa
|
jaby_engine_fconv $< -o $@ xa
|
||||||
|
|
|
@ -47,6 +47,11 @@
|
||||||
<Channel>../assets/bin/fox.xa</Channel>
|
<Channel>../assets/bin/fox.xa</Channel>
|
||||||
</InterleavedFile>
|
</InterleavedFile>
|
||||||
</Directory>
|
</Directory>
|
||||||
|
<Directory name = "SFX" hidden = "true">
|
||||||
|
<!--TODO: test lz4 version-->
|
||||||
|
<File name = "APPLE.VAG" lz4 = "none">../assets/bin/apple.vag</File>
|
||||||
|
<File name = "BLUBB.VAG" lz4 = "none">../assets/bin/blubb.vag</File>
|
||||||
|
</Directory>
|
||||||
</Filesystem>
|
</Filesystem>
|
||||||
<AudioTrack align = "true">../assets/audio/temp/breaking.wav</AudioTrack>
|
<AudioTrack align = "true">../assets/audio/temp/breaking.wav</AudioTrack>
|
||||||
</PSXCD>
|
</PSXCD>
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../jabyengine_defines.hpp"
|
||||||
|
|
||||||
|
namespace JabyEngine {
|
||||||
|
// Taken from boost endian
|
||||||
|
|
||||||
|
static constexpr uint8_t read_be(uint8_t x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint16_t read_be(uint16_t x) {
|
||||||
|
return (x << 8) | (x >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint32_t read_be(uint32_t x) {
|
||||||
|
const uint32_t step16 = x << 16 | x >> 16;
|
||||||
|
return ((step16 << 8) & 0xff00ff00) | ((step16 >> 8) & 0x00ff00ff);
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,4 +34,14 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr T min_of(T a, T b) {
|
||||||
|
return (a < b) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr T max_of(T a, T b) {
|
||||||
|
return (a > b) ? a : b;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stddef.hpp>
|
||||||
|
|
||||||
|
namespace JabyEngine {
|
||||||
|
using word_t = uint32_t;
|
||||||
|
|
||||||
|
static constexpr size_t bytes_to_words(size_t bytes) {
|
||||||
|
return bytes/sizeof(word_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr size_t words_to_bytes(size_t words) {
|
||||||
|
return words*sizeof(word_t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ namespace JabyEngine {
|
||||||
Reserved reserved;
|
Reserved reserved;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static __always_inline State from(const T& state, const uint8_t* data_adr, GenericProcessRoutine<T> process_routine) {
|
static __always_inline State from(const T& state, const uint32_t* data_adr, GenericProcessRoutine<T> process_routine) {
|
||||||
return {Configuration::from(process_routine, data_adr), *reinterpret_cast<const Reserved*>(&state)};
|
return {Configuration::from(process_routine, reinterpret_cast<const uint8_t*>(data_adr)), *reinterpret_cast<const Reserved*>(&state)};
|
||||||
static_assert(sizeof(T) <= sizeof(Reserved));
|
static_assert(sizeof(T) <= sizeof(Reserved));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ namespace JabyEngine {
|
||||||
// The nothing state
|
// The nothing state
|
||||||
State create(const uint32_t* data_adr, const Nothing& nothing);
|
State create(const uint32_t* data_adr, const Nothing& nothing);
|
||||||
State create(const uint32_t* data_adr, const SimpleTIM& file);
|
State create(const uint32_t* data_adr, const SimpleTIM& file);
|
||||||
|
State create(const uint32_t* data_adr, const VAG& file);
|
||||||
|
|
||||||
State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload);
|
State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <PSX/jabyengine_config.hpp>
|
||||||
#include "../Overlay/overlay.hpp"
|
#include "../Overlay/overlay.hpp"
|
||||||
#include "file_types.hpp"
|
#include "file_types.hpp"
|
||||||
|
|
||||||
|
@ -7,8 +8,11 @@ namespace JabyEngine {
|
||||||
using RawPayload_t = uint32_t;
|
using RawPayload_t = uint32_t;
|
||||||
|
|
||||||
enum struct CDFileType : CDFileType_t {
|
enum struct CDFileType : CDFileType_t {
|
||||||
SimpleTIM = 0,
|
CopyTo = 0,
|
||||||
CopyTo,
|
SimpleTIM,
|
||||||
|
#ifdef __SUPPORT_VAG__
|
||||||
|
SonyVAG,
|
||||||
|
#endif // __SUPPORT_VAG__
|
||||||
Custom,
|
Custom,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,8 +20,9 @@ namespace JabyEngine {
|
||||||
struct CDFile {
|
struct CDFile {
|
||||||
union Payload {
|
union Payload {
|
||||||
RawPayload_t raw;
|
RawPayload_t raw;
|
||||||
SimpleTIM simple_tim;
|
|
||||||
CopyTo copy_to;
|
CopyTo copy_to;
|
||||||
|
SimpleTIM simple_tim;
|
||||||
|
VAG vag;
|
||||||
Overlay overlay;
|
Overlay overlay;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,13 +43,19 @@ namespace JabyEngine {
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
struct CDFileBuilder {
|
struct CDFileBuilder {
|
||||||
|
static constexpr CDFile copy_to(uint8_t rel_lba_idx, uint32_t* dst) {
|
||||||
|
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.copy_to = CopyTo{dst}}};
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr CDFile simple_tim(uint8_t rel_lba_idx, SimpleTIM simple_tim) {
|
static constexpr CDFile simple_tim(uint8_t rel_lba_idx, SimpleTIM simple_tim) {
|
||||||
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::SimpleTIM, .payload = {.simple_tim = simple_tim}};
|
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::SimpleTIM, .payload = {.simple_tim = simple_tim}};
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr CDFile copy_to(uint8_t rel_lba_idx, uint32_t* dst) {
|
#ifdef __SUPPORT_VAG__
|
||||||
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.copy_to = CopyTo{dst}}};
|
static constexpr CDFile sony_vag(uint8_t lba_idx, VAG vag) {
|
||||||
|
return CDFile{.rel_lba_idx = lba_idx, .type = CDFileType::SonyVAG, .payload = {.vag = vag}};
|
||||||
}
|
}
|
||||||
|
#endif //__SUPPORT_VAG__
|
||||||
|
|
||||||
static constexpr CDFile overlay(uint8_t rel_lba_idx, uint32_t* overlay_dst) {
|
static constexpr CDFile overlay(uint8_t rel_lba_idx, uint32_t* overlay_dst) {
|
||||||
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.overlay = Overlay{overlay_dst}}};
|
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.overlay = Overlay{overlay_dst}}};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Processor/file_processor.hpp"
|
#include "Processor/file_processor.hpp"
|
||||||
#include "cd_file_types.hpp"
|
#include "cd_file_types.hpp"
|
||||||
|
#include <limits.hpp>
|
||||||
|
|
||||||
namespace JabyEngine {
|
namespace JabyEngine {
|
||||||
namespace FileProcessor {
|
namespace FileProcessor {
|
||||||
|
@ -18,6 +19,54 @@ namespace JabyEngine {
|
||||||
config.process_routine = reinterpret_cast<State::ProcessRoutine>(process_routine);
|
config.process_routine = reinterpret_cast<State::ProcessRoutine>(process_routine);
|
||||||
return process_routine(config, state);
|
return process_routine(config, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace DMA {
|
||||||
|
using words_t = size_t;
|
||||||
|
|
||||||
|
struct WordsReady {
|
||||||
|
uint32_t words_to_use;
|
||||||
|
bool is_last;
|
||||||
|
|
||||||
|
static constexpr WordsReady calculate(const State::Configuration& config, words_t words_left) {
|
||||||
|
const auto config_data_words = (config.data_bytes/sizeof(uint32_t));
|
||||||
|
const auto words_to_use = (config_data_words > words_left) ? words_left : config_data_words;
|
||||||
|
|
||||||
|
return {
|
||||||
|
.words_to_use = words_to_use,
|
||||||
|
.is_last = words_to_use == words_left
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static words_t send_words(words_t words_to_send, bool send_all) {
|
||||||
|
auto blocks_to_send = words_to_send/16;
|
||||||
|
while(blocks_to_send > 0) {
|
||||||
|
const auto block_send = (blocks_to_send > UI16_MAX) ? UI16_MAX : blocks_to_send;
|
||||||
|
|
||||||
|
// Send data!
|
||||||
|
T::wait();
|
||||||
|
T::Receive::start(blocks_to_send);
|
||||||
|
blocks_to_send -= block_send;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(send_all) {
|
||||||
|
const auto last_words_to_send = (words_to_send & 0b1111);
|
||||||
|
if(last_words_to_send > 0) {
|
||||||
|
T::wait();
|
||||||
|
T::Receive::start(1, last_words_to_send);
|
||||||
|
}
|
||||||
|
|
||||||
|
T::wait();
|
||||||
|
T::end();
|
||||||
|
return words_to_send;
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
return (words_to_send & ~0b1111);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,12 +2,19 @@
|
||||||
#include "../Auxiliary/bits.hpp"
|
#include "../Auxiliary/bits.hpp"
|
||||||
#include "../GPU/gpu_types.hpp"
|
#include "../GPU/gpu_types.hpp"
|
||||||
#include "../jabyengine_defines.hpp"
|
#include "../jabyengine_defines.hpp"
|
||||||
|
#include "../SPU/spu.hpp"
|
||||||
|
|
||||||
namespace JabyEngine {
|
namespace JabyEngine {
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct Nothing {
|
struct Nothing {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CopyTo {
|
||||||
|
uint32_t* dst;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Call TIM?
|
||||||
|
// TODO: Add create function?
|
||||||
struct SimpleTIM {
|
struct SimpleTIM {
|
||||||
static constexpr auto TextureX = BitRange::from_to(0, 8);
|
static constexpr auto TextureX = BitRange::from_to(0, 8);
|
||||||
static constexpr auto TextureY = BitRange::from_to(9, 16);
|
static constexpr auto TextureY = BitRange::from_to(9, 16);
|
||||||
|
@ -61,9 +68,15 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CopyTo {
|
struct VAG {
|
||||||
uint32_t* dst;
|
uint8_t voice_number;
|
||||||
|
SPU::SimpleVolume inital_stereo_vol;
|
||||||
|
|
||||||
|
static constexpr VAG create(uint8_t voice_num, SPU::SimpleVolume volume) {
|
||||||
|
return VAG{.voice_number = voice_num, .inital_stereo_vol = volume};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
typedef CopyTo Overlay;
|
typedef CopyTo Overlay;
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../System/IOPorts/spu_io.hpp"
|
||||||
|
|
||||||
|
namespace JabyEngine {
|
||||||
|
namespace SPU {
|
||||||
|
using SPU_IO::operator""_vol;
|
||||||
|
|
||||||
|
using SRAMAdr = SPU_IO::SRAMAdr;
|
||||||
|
using SimpleVolume = SPU_IO::SimpleVolume;
|
||||||
|
using SweepVolume = SPU_IO::SweepVolume;
|
||||||
|
|
||||||
|
// TODO: Rename to sample...?
|
||||||
|
struct Voice {
|
||||||
|
size_t get_id() const {
|
||||||
|
return reinterpret_cast<size_t>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SRAMAdr allocate(size_t size);
|
||||||
|
SRAMAdr allocate(SPU_IO::SampleRate frequency, size_t size);
|
||||||
|
void deallocate();
|
||||||
|
|
||||||
|
void set_sample_rate(SPU_IO::SampleRate frequency) {
|
||||||
|
SPU_IO::Voice[Voice::get_id()].sampleRate.write(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_volume(SimpleVolume left, SimpleVolume right) {
|
||||||
|
// TODO: Verify that assembly code is super simple
|
||||||
|
SPU_IO::Voice[Voice::get_id()].volumeLeft.write(SweepVolume::create(left));
|
||||||
|
SPU_IO::Voice[Voice::get_id()].volumeRight.write(SweepVolume::create(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
void play() {
|
||||||
|
SPU_IO::Key::On.write(SPU_IO::KeyOn::for_specific(Voice::get_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void play_if_end() {
|
||||||
|
if(Voice::is_end()) {
|
||||||
|
Voice::play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
SPU_IO::Key::Off.write(SPU_IO::KeyOff::for_specific(Voice::get_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_end() const {
|
||||||
|
return SPU_IO::Voice[Voice::get_id()].adsr_volume.read() == SimpleVolume::mute();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto& voice = __new_declare_io_port_array(Voice, SPU_IO::VoiceCount, 0x0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,214 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../ioport.hpp"
|
||||||
|
#include <limits.hpp>
|
||||||
|
|
||||||
|
namespace JabyEngine {
|
||||||
|
namespace SPU_IO_Values {
|
||||||
|
namespace MemoryMap {
|
||||||
|
static constexpr uintptr_t ADPCM = 0x01000;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declare_io_value(AD, uint16_t) {
|
||||||
|
static constexpr auto AttackMode = Bit(15);
|
||||||
|
static constexpr auto AttackShift = BitRange::from_to(10, 14);
|
||||||
|
static constexpr auto AttackStep = BitRange::from_to(8, 9);
|
||||||
|
static constexpr auto DecayShift = BitRange::from_to(4, 7);
|
||||||
|
static constexpr auto SustainLevel = BitRange::from_to(0, 3);
|
||||||
|
|
||||||
|
static constexpr AD none() {
|
||||||
|
return AD{0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(ControlRegister, uint16_t) {
|
||||||
|
enum RAMTransferMode {
|
||||||
|
Stop = 0,
|
||||||
|
ManualWrite = 1,
|
||||||
|
DMAWrite = 2,
|
||||||
|
DMARead = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr auto Enable = Bit(15);
|
||||||
|
static constexpr auto Unmute = Bit(14);
|
||||||
|
static constexpr auto NoiseFrequcenyShift = BitRange::from_to(10, 13);
|
||||||
|
static constexpr auto NoiseFrequcenyStep = BitRange::from_to(8, 9);
|
||||||
|
static constexpr auto ReverbMasterEnable = Bit(7);
|
||||||
|
static constexpr auto IRQ9Enable = Bit(6);
|
||||||
|
static constexpr auto TransferMode = BitRange::from_to(4, 5);
|
||||||
|
static constexpr auto ExternalAudioReverb = Bit(3);
|
||||||
|
static constexpr auto CDAudioReverb = Bit(2);
|
||||||
|
static constexpr auto ExternalAudioEnable = Bit(1);
|
||||||
|
static constexpr auto CDAudioEnable = Bit(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(DataTransferControl, uint16_t) {
|
||||||
|
static constexpr DataTransferControl NormalTransferMode() {
|
||||||
|
return DataTransferControl{0x0004};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(Echo, uint32_t) {
|
||||||
|
static constexpr auto EchoBits = BitRange::from_to(0, 23);
|
||||||
|
|
||||||
|
static constexpr Echo AllOff() {
|
||||||
|
return Echo{0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(KeyOff, uint32_t) {
|
||||||
|
static constexpr KeyOff for_specific(uint32_t id) {
|
||||||
|
return KeyOff{1u << id};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr KeyOff all() {
|
||||||
|
return KeyOff{UI32_MAX};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(KeyOn, uint32_t) {
|
||||||
|
static constexpr KeyOn for_specific(uint32_t id) {
|
||||||
|
return KeyOn{1u << id};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr KeyOn all() {
|
||||||
|
return KeyOn{UI32_MAX};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(KeyStatus, uint32_t) {
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(Noise, uint16_t) {
|
||||||
|
static constexpr auto NoiseBits = BitRange::from_to(0, 23);
|
||||||
|
|
||||||
|
static constexpr Noise AllOff() {
|
||||||
|
return Noise{0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(PitchModulation, uint32_t) {
|
||||||
|
static constexpr auto EnableBits = BitRange::from_to(1, 23);
|
||||||
|
|
||||||
|
static constexpr PitchModulation AllOff() {
|
||||||
|
return PitchModulation{0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(SampleRate, uint16_t) {
|
||||||
|
static constexpr SampleRate stop() {
|
||||||
|
return SampleRate{0};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr SampleRate from_HZ(uint32_t freq) {
|
||||||
|
constexpr uint32_t Base1024Hz = static_cast<uint32_t>((4096.0/44100.0)*1024.0);
|
||||||
|
return SampleRate{static_cast<uint16_t>((freq >> 10)*Base1024Hz)};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr SampleRate from_HZ(double freq) {
|
||||||
|
//4096 == 44100Hz
|
||||||
|
constexpr double Base = (4096.0 / 44100.0);
|
||||||
|
return SampleRate{static_cast<uint16_t>((freq*Base))};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(SimpleVolume, int16_t) {
|
||||||
|
static constexpr auto MaxVolume = I16_MAX;
|
||||||
|
|
||||||
|
static constexpr SimpleVolume mute() {
|
||||||
|
return SimpleVolume{0};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator int16_t() const {
|
||||||
|
return this->raw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr SimpleVolume operator""_vol(long double fraction) {
|
||||||
|
return {static_cast<int16_t>(static_cast<long double>(SimpleVolume::MaxVolume)*fraction)};
|
||||||
|
}
|
||||||
|
|
||||||
|
__declare_io_value(SR, uint16_t) {
|
||||||
|
static constexpr auto SustainMode = Bit(31 - 16);
|
||||||
|
static constexpr auto SustainDirection = Bit(30 - 16);
|
||||||
|
static constexpr auto SustainShift = BitRange::from_to((24 - 16), (28 - 16));
|
||||||
|
static constexpr auto SustainStep = BitRange::from_to((22 - 16), (23 - 16));
|
||||||
|
static constexpr auto ReleaseMode = Bit(21 - 16);
|
||||||
|
static constexpr auto ReleaseShift = BitRange::from_to((16 - 16), (20 - 16));
|
||||||
|
|
||||||
|
static constexpr SR none() {
|
||||||
|
return SR{0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(SRAMAdr, uint16_t) {
|
||||||
|
static constexpr SRAMAdr null() {
|
||||||
|
return SRAMAdr{0x0};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr SRAMAdr adpcm_start() {
|
||||||
|
return SRAMAdr{MemoryMap::ADPCM};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(StatusRegister, uint16_t) {
|
||||||
|
enum CapureBufferHalf {
|
||||||
|
First = 0,
|
||||||
|
Second = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr auto Unused = BitRange::from_to(12, 15);
|
||||||
|
static constexpr auto CaputreBufferHalf = Bit(11);
|
||||||
|
static constexpr auto TransferBusy = Bit(10);
|
||||||
|
static constexpr auto IsDMARead = Bit(9);
|
||||||
|
static constexpr auto isDMAWrite = Bit(8);
|
||||||
|
static constexpr auto isDMA = Bit(7);
|
||||||
|
static constexpr auto isIRQ = Bit(6);
|
||||||
|
// Copies of ControlRegister
|
||||||
|
static constexpr auto TransferMode = BitRange::from_to(4, 5);
|
||||||
|
static constexpr auto ExternalAudioReverb = Bit(3);
|
||||||
|
static constexpr auto CDAudioReverb = Bit(2);
|
||||||
|
static constexpr auto ExternalAudioEnable = Bit(1);
|
||||||
|
static constexpr auto CDAudioEnable = Bit(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
__declare_io_value(SweepVolume, int16_t) {
|
||||||
|
struct VolumeMode {
|
||||||
|
static constexpr auto MaxVolume = (I16_MAX >> 1);
|
||||||
|
static constexpr auto EnableSweep = Bit(15);
|
||||||
|
static constexpr auto Enable = !EnableSweep;
|
||||||
|
static constexpr auto Volume = BitRange::from_to(0, 14);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SweepMode {
|
||||||
|
enum Mode {
|
||||||
|
Linear = 0,
|
||||||
|
Exponential = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Direction {
|
||||||
|
Increase = 0,
|
||||||
|
Decrease = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Phase {
|
||||||
|
Posititve = 0,
|
||||||
|
Negative = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr auto Mode = Bit(14);
|
||||||
|
static constexpr auto Direction = Bit(13);
|
||||||
|
static constexpr auto Phase = Bit(12);
|
||||||
|
static constexpr auto Shift = BitRange::from_to(2, 6);
|
||||||
|
static constexpr auto Step = BitRange::from_to(0, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr SweepVolume create(SimpleVolume volume) {
|
||||||
|
return from(VolumeMode::Enable, VolumeMode::Volume.with(volume.raw >> 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr SweepVolume mute() {
|
||||||
|
return SweepVolume{0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,17 +65,9 @@ namespace JabyEngine {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ubus32_t {
|
|
||||||
uint16_t low;
|
|
||||||
uint16_t high;
|
|
||||||
|
|
||||||
static ubus32_t from(uint32_t value) {
|
|
||||||
return {.low = static_cast<uint16_t>(value & 0xFFFF), .high = static_cast<uint16_t>(value >> 16)};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct IOPort {
|
struct IOPort {
|
||||||
|
using Value = T;
|
||||||
T value;
|
T value;
|
||||||
|
|
||||||
T read() const {
|
T read() const {
|
||||||
|
@ -88,24 +80,34 @@ namespace JabyEngine {
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct IOPort<ubus32_t> {
|
struct IOPort<uint32_t>;
|
||||||
ubus32_t value;
|
|
||||||
|
|
||||||
ubus32_t read() const {
|
template<typename T>
|
||||||
auto*const cv_this = const_cast<const volatile IOPort<ubus32_t>*>(this);
|
struct IOPort32 {
|
||||||
|
union ValueHelper {
|
||||||
return {.low = cv_this->value.low, .high = cv_this->value.high};
|
struct {
|
||||||
|
uint16_t low;
|
||||||
|
uint16_t high;
|
||||||
|
};
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
using Value = T;
|
||||||
|
T value;
|
||||||
|
|
||||||
|
T read() const {
|
||||||
|
const auto* cast_this = reinterpret_cast<const IOPort32<ValueHelper>*>(this);
|
||||||
|
const volatile auto* cv_this = const_cast<volatile decltype(cast_this)>(cast_this);
|
||||||
|
|
||||||
|
return ValueHelper{.low = cv_this->value.low, .high = cv_this->value.high}.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(ubus32_t value) {
|
void write(T value) {
|
||||||
auto*const cv_this = const_cast<volatile IOPort<ubus32_t>*>(this);
|
const auto new_value = ValueHelper{.value = value};
|
||||||
|
auto* cast_this = reinterpret_cast<IOPort32<ValueHelper>*>(this);
|
||||||
|
volatile auto* v_this = const_cast<volatile decltype(cast_this)>(cast_this);
|
||||||
|
|
||||||
cv_this->value.low = value.low;
|
v_this->value.low = new_value.low;
|
||||||
cv_this->value.high = value.high;
|
v_this->value.high = new_value.high;
|
||||||
}
|
|
||||||
|
|
||||||
void write(uint32_t value) {
|
|
||||||
IOPort<ubus32_t>::write(ubus32_t::from(value));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,4 +117,7 @@ namespace JabyEngine {
|
||||||
#define __declare_io_port_w_type(cv, type, name, adr) __declare_value_at(cv, ::JabyEngine::IOPort<type>, name, adr)
|
#define __declare_io_port_w_type(cv, type, name, adr) __declare_value_at(cv, ::JabyEngine::IOPort<type>, name, adr)
|
||||||
#define __declare_io_port(cv, name, adr) __declare_io_port_w_type(cv, struct name, name, adr)
|
#define __declare_io_port(cv, name, adr) __declare_io_port_w_type(cv, struct name, name, adr)
|
||||||
#define __declare_io_port_array(cv, name, size, adr) __declare_array_at(cv, struct name, name, size, adr)
|
#define __declare_io_port_array(cv, name, size, adr) __declare_array_at(cv, struct name, name, size, adr)
|
||||||
|
|
||||||
|
#define __new_declare_io_port(type, adr) *reinterpret_cast<type*>(adr)
|
||||||
|
#define __new_declare_io_port_array(type, size, adr) reinterpret_cast<type(&)[size]>(*reinterpret_cast<type*>(adr))
|
||||||
}
|
}
|
|
@ -1,172 +1,88 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "ioport.hpp"
|
#include "IOValues/spu_io_values.hpp"
|
||||||
#include <limits.hpp>
|
#include <limits.hpp>
|
||||||
|
|
||||||
namespace JabyEngine {
|
namespace JabyEngine {
|
||||||
namespace SPU_IO {
|
namespace SPU_IO {
|
||||||
enum struct Mode {
|
using namespace SPU_IO_Values;
|
||||||
Linear = 0,
|
|
||||||
Exponential = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum struct Direction {
|
static constexpr size_t VoiceCount = 24;
|
||||||
Increase = 0,
|
static constexpr size_t ReverbCount = 1;
|
||||||
Decrease = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum struct Phase {
|
struct ControlRegisterIO : public IOPort<SPU_IO_Values::ControlRegister> {
|
||||||
Posititve = 0,
|
using TransferMode = Value::RAMTransferMode;
|
||||||
Negative = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
//0..0x1F = Fast..Slow
|
void set_transfer_mode(TransferMode mode) {
|
||||||
typedef uint8_t Shift;
|
this->write(this->read().set(ControlRegister::TransferMode.with(mode)));
|
||||||
|
while(this->read().get(ControlRegister::TransferMode) != mode);
|
||||||
//0..3 = +7, +6, +5, +4 or -6, -7, -6, -5
|
|
||||||
typedef uint8_t Step;
|
|
||||||
|
|
||||||
__declare_io_value(DataTransferControl, uint16_t) {
|
|
||||||
};
|
|
||||||
|
|
||||||
__declare_io_value(SimpleVolume, int16_t) {
|
|
||||||
constexpr operator int16_t() const {
|
|
||||||
return this->raw;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
__declare_io_value(Adr, uint16_t) {
|
|
||||||
};
|
|
||||||
|
|
||||||
__declare_io_value(SampleRate, uint16_t) {
|
|
||||||
static constexpr SampleRate from_HZ(double freq) {
|
|
||||||
//4096 == 44100Hz
|
|
||||||
constexpr double Base = (4096.0 / 44100.0);
|
|
||||||
|
|
||||||
return {static_cast<uint16_t>((freq*Base))};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
__declare_io_value(SweepVolume, int16_t) {
|
using ADIO = IOPort<SPU_IO_Values::AD>;
|
||||||
// For Volume Mode
|
using DataTransferControlIO = IOPort<SPU_IO_Values::DataTransferControl>;
|
||||||
static constexpr auto SweepEnable = Bit(15);
|
using EchoIO = IOPort32<SPU_IO_Values::Echo>;
|
||||||
static constexpr auto VolumeEnable = !SweepEnable;
|
using KeyOffIO = IOPort32<SPU_IO_Values::KeyOff>;
|
||||||
static constexpr auto Volume = BitRange::from_to(0, 14);
|
using KeyOnIO = IOPort32<SPU_IO_Values::KeyOn>;
|
||||||
|
using KeyStatusIO = IOPort32<SPU_IO_Values::KeyStatus>;
|
||||||
// For Sweep Mode
|
using NoiseIO = IOPort<SPU_IO_Values::Noise>;
|
||||||
static constexpr auto SweepMode = Bit(14);
|
using PitchModulationIO = IOPort32<SPU_IO_Values::PitchModulation>;
|
||||||
static constexpr auto SweepDirection = Bit(13);
|
using SampleRateIO = IOPort<SPU_IO_Values::SampleRate>;
|
||||||
static constexpr auto SweepPhase = Bit(12);
|
using SimpleVolumeIO = IOPort<SPU_IO_Values::SimpleVolume>;
|
||||||
static constexpr auto SweepShift = BitRange::from_to(2, 6);
|
using StatusRegisterIO = IOPort<SPU_IO_Values::StatusRegister>;
|
||||||
static constexpr auto SweepStep = BitRange::from_to(0, 1);
|
using SRIO = IOPort<SPU_IO_Values::SR>;
|
||||||
};
|
using SRAMAdrIO = IOPort<SPU_IO_Values::SRAMAdr>;
|
||||||
|
using SweepVolumeIO = IOPort<SweepVolume>;
|
||||||
__declare_io_value(SR, uint16_t) {
|
|
||||||
static constexpr auto SustainMode = Bit(31 - 16);
|
|
||||||
static constexpr auto SustainDirection = Bit(30 - 16);
|
|
||||||
static constexpr auto SustainShift = BitRange::from_to((24 - 16), (28 - 16));
|
|
||||||
static constexpr auto SustainStep = BitRange::from_to((22 - 16), (23 - 16));
|
|
||||||
static constexpr auto ReleaseMode = Bit(21 - 16);
|
|
||||||
static constexpr auto ReleaseShift = BitRange::from_to((16 - 16), (20 - 16));
|
|
||||||
};
|
|
||||||
|
|
||||||
__declare_io_value(AD, uint16_t) {
|
|
||||||
static constexpr auto AttackMode = Bit(15);
|
|
||||||
static constexpr auto AttackShift = BitRange::from_to(10, 14);
|
|
||||||
static constexpr auto AttackStep = BitRange::from_to(8, 9);
|
|
||||||
static constexpr auto DecayShift = BitRange::from_to(4, 7);
|
|
||||||
static constexpr auto SustainLevel = BitRange::from_to(0, 3);
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct Voice {
|
struct Voice {
|
||||||
IOPort<SweepVolume> volumeLeft; //Offset: 0x0
|
SweepVolumeIO volumeLeft; //Offset: 0x0
|
||||||
IOPort<SweepVolume> volumeRight; //Offset: 0x2
|
SweepVolumeIO volumeRight; //Offset: 0x2
|
||||||
IOPort<SampleRate> sampleRate; //Offset: 0x4;
|
SampleRateIO sampleRate; //Offset: 0x4;
|
||||||
IOPort<Adr> adr; //Offset: 0x6
|
SRAMAdrIO adr; //Offset: 0x6
|
||||||
IOPort<AD> ad; //Offset: 0x8
|
ADIO ad; //Offset: 0x8
|
||||||
IOPort<SR> sr; //Offset: 0xA
|
SRIO sr; //Offset: 0xA
|
||||||
IOPort<SimpleVolume> currentVolume; //Offset: 0xC
|
SimpleVolumeIO adsr_volume; //Offset: 0xC
|
||||||
IOPort<Adr> repeatAdr; //Offset: 0xE
|
SRAMAdrIO repeatAdr; //Offset: 0xE
|
||||||
|
|
||||||
static constexpr Adr start_adr() {
|
|
||||||
return {0x200};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
__declare_io_value(ControlRegister, uint16_t) {
|
static auto& Voice = __new_declare_io_port_array(struct Voice, VoiceCount, 0x1F801C00);
|
||||||
enum RAMTransferMode {
|
static auto& PMON = __new_declare_io_port(PitchModulationIO, 0x1F801D90);
|
||||||
Stop = 0,
|
static auto& NON = __new_declare_io_port(NoiseIO, 0x1F801D94);
|
||||||
ManualWrite = 1,
|
static auto& EON = __new_declare_io_port(EchoIO, 0x1F801D98);
|
||||||
DMAWrite = 2,
|
static auto& SRAMTransferAdr = __new_declare_io_port(SRAMAdrIO, 0x1F801DA6);
|
||||||
DMARead = 3
|
static auto& ControlRegister = __new_declare_io_port(ControlRegisterIO, 0x1F801DAA);
|
||||||
};
|
static auto& DataTransferControl = __new_declare_io_port(DataTransferControlIO, 0x1F801DAC);
|
||||||
|
static auto& StatusRegister = __new_declare_io_port(StatusRegisterIO, 0x1F801DAE);
|
||||||
static constexpr auto Enable = Bit(15);
|
|
||||||
static constexpr auto Unmute = Bit(14);
|
|
||||||
static constexpr auto NoiseFrequcenyShift = BitRange::from_to(10, 13);
|
|
||||||
static constexpr auto NoiseFrequcenyStep = BitRange::from_to(8, 9);
|
|
||||||
static constexpr auto ReverbMasterEnable = Bit(7);
|
|
||||||
static constexpr auto IRQ9Enable = Bit(6);
|
|
||||||
static constexpr auto TransferMode = BitRange::from_to(4, 5);
|
|
||||||
static constexpr auto ExternalAudioReverb = Bit(3);
|
|
||||||
static constexpr auto CDAudioReverb = Bit(2);
|
|
||||||
static constexpr auto ExternalAudioEnable = Bit(1);
|
|
||||||
static constexpr auto CDAudioEnable = Bit(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
__declare_io_value(PMON, uint16_t) {
|
|
||||||
static constexpr auto EnableBits = BitRange::from_to(1, 23);
|
|
||||||
};
|
|
||||||
|
|
||||||
__declare_io_value(NON, uint16_t) {
|
|
||||||
static constexpr auto NoiseBits = BitRange::from_to(0, 23);
|
|
||||||
};
|
|
||||||
|
|
||||||
__declare_io_value(EON, uint16_t) {
|
|
||||||
static constexpr auto EchoBits = BitRange::from_to(0, 23);
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr size_t VoiceCount = 24;
|
|
||||||
|
|
||||||
struct Key {
|
|
||||||
__declare_io_port_w_type(inline, ubus32_t, On, 0x1F801D88);
|
|
||||||
__declare_io_port_w_type(inline, ubus32_t, Off, 0x1F801D8C);
|
|
||||||
__declare_io_port_w_type(inline, ubus32_t, Status, 0x1F801D9C);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MainVolume {
|
|
||||||
__declare_io_port_w_type(inline, SweepVolume, Left, 0x1F801D80);
|
|
||||||
__declare_io_port_w_type(inline, SweepVolume, Right, 0x1F801D82);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CDVolume {
|
struct CDVolume {
|
||||||
__declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801DB0);
|
static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB0);
|
||||||
__declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801DB2);
|
static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB2);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExternalAudioInputVolume {
|
struct ExternalAudioInputVolume {
|
||||||
__declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801DB4);
|
static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB4);
|
||||||
__declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801DB6);
|
static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB6);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Key {
|
||||||
|
static inline auto& On = __new_declare_io_port(KeyOnIO, 0x1F801D88);
|
||||||
|
static inline auto& Off = __new_declare_io_port(KeyOffIO, 0x1F801D8C);
|
||||||
|
static inline auto& Status = __new_declare_io_port(KeyStatusIO, 0x1F801D9C);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MainVolume {
|
||||||
|
static inline auto& Left = __new_declare_io_port(SweepVolumeIO, 0x1F801D80);
|
||||||
|
static inline auto& Right = __new_declare_io_port(SweepVolumeIO, 0x1F801D82);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Reverb {
|
struct Reverb {
|
||||||
struct Volume {
|
struct Volume {
|
||||||
__declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801D84);
|
static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801D84);
|
||||||
__declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801D86);
|
static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801D86);
|
||||||
};
|
};
|
||||||
__declare_io_port_w_type(inline, Adr, WorkAreaAdr, 0x1F801DA2);
|
static inline auto& On = EON;
|
||||||
|
static inline auto& WorkAreaAdr = __new_declare_io_port(SRAMAdrIO, 0x1F801DA2);
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr SimpleVolume operator""_vol(long double fraction) {
|
|
||||||
return {static_cast<int16_t>(static_cast<long double>(I16_MAX)*fraction)};
|
|
||||||
}
|
|
||||||
|
|
||||||
__declare_io_port(, ControlRegister, 0x1F801DAA);
|
|
||||||
__declare_io_port(, DataTransferControl, 0x1F801DAC);
|
|
||||||
__declare_io_port(, PMON, 0x1F801D90);
|
|
||||||
__declare_io_port(, NON, 0x1F801D94);
|
|
||||||
__declare_io_port(, EON, 0x1F801D98);
|
|
||||||
|
|
||||||
__declare_io_port_array(, Voice, VoiceCount, 0x1F801C00);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -31,6 +31,8 @@ namespace JabyEngine {
|
||||||
using Configuration = CustomConfiguration;
|
using Configuration = CustomConfiguration;
|
||||||
#else
|
#else
|
||||||
using Configuration = DefaultConfiguration;
|
using Configuration = DefaultConfiguration;
|
||||||
|
// TODO: v Turn off when this is supported
|
||||||
|
#define __SUPPORT_VAG__
|
||||||
#define __SUPPORT_PS3__
|
#define __SUPPORT_PS3__
|
||||||
#endif // has jabyengine_custom_config
|
#endif // has jabyengine_custom_config
|
||||||
}
|
}
|
|
@ -55,7 +55,7 @@ __stack_start = ORIGIN(ram) + LENGTH(ram);
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
.zero (NOLOAD) : {
|
.zero (NOLOAD) : {
|
||||||
_ZN10JabyEngine2CD4zeroE = .;
|
_ZN10JabyEngine3SPU5voiceE = .;
|
||||||
} > zero
|
} > zero
|
||||||
|
|
||||||
.bios (NOLOAD) : {
|
.bios (NOLOAD) : {
|
||||||
|
|
11
readme.md
11
readme.md
|
@ -9,11 +9,18 @@
|
||||||
- [ ] Emulator
|
- [ ] Emulator
|
||||||
- [ ] Real Hardware
|
- [ ] Real Hardware
|
||||||
|
|
||||||
# Todo
|
# TODO
|
||||||
- [ ] Ko-fi supporter list
|
- [ ] Ko-fi supporter list
|
||||||
- [ ] Support more GTE
|
- [ ] Support more GTE
|
||||||
- [X] Easy serial code swap
|
- [X] Easy serial code swap
|
||||||
- [X] Support .subst files to be substituted with environment variables
|
- [X] Support .subst files to be substituted with environment variables
|
||||||
- [X] Support pop-fe
|
- [X] Support pop-fe
|
||||||
- [ ] PS3 PKG generation tool?
|
- [ ] PS3 PKG generation tool?
|
||||||
- [ ] PS3 runtime detection?
|
- [ ] PS3 runtime detection?
|
||||||
|
- [ ] Move DMA code to public include for custom loading of files?
|
||||||
|
- [ ] Maybe make it an interface with SPU/GPU as a specification...?
|
||||||
|
- [ ] Could be empty classes that the linker maps to 0 or somewhere
|
||||||
|
- [ ] Could be a all static struct (I like that better; We are not Nicolas Noble)
|
||||||
|
- [ ] Redo the IO ports again...?
|
||||||
|
- [ ] Support better file loading with threads
|
||||||
|
- [ ] Loading Screen with GPU IO? (Does DMA and IO work together?)
|
|
@ -65,21 +65,21 @@ namespace JabyEngine {
|
||||||
GPU_IO::GP1.write(GPU_IO::Command::ResetCMDBufer());
|
GPU_IO::GP1.write(GPU_IO::Command::ResetCMDBufer());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DMA {
|
struct DMA {
|
||||||
#ifdef __SUPPORT_PS3__
|
#ifdef __SUPPORT_PS3__
|
||||||
// The PS3 doesn't autoincrement the GPU MADR register so we have to do it
|
// The PS3 doesn't autoincrement the GPU MADR register so we have to do it
|
||||||
extern uintptr_t MADR;
|
static uintptr_t MADR;
|
||||||
#endif // __SUPPORT_PS3__
|
#endif // __SUPPORT_PS3__
|
||||||
|
|
||||||
static void wait() {
|
static void wait() {
|
||||||
::JabyEngine::DMA_IO::GPU.wait();
|
DMA_IO::GPU.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void end() {
|
static void end() {
|
||||||
reset_cmd_buffer();
|
reset_cmd_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Receive {
|
struct Receive {
|
||||||
static void prepare() {
|
static void prepare() {
|
||||||
GPU_IO::GP1.write(GPU_IO::Command::DMADirection(GPU_IO::DMADirection::CPU2GPU));
|
GPU_IO::GP1.write(GPU_IO::Command::DMADirection(GPU_IO::DMADirection::CPU2GPU));
|
||||||
reset_cmd_buffer();
|
reset_cmd_buffer();
|
||||||
|
@ -87,7 +87,7 @@ namespace JabyEngine {
|
||||||
|
|
||||||
static void set_src(uintptr_t adr) {
|
static void set_src(uintptr_t adr) {
|
||||||
#ifdef __SUPPORT_PS3__
|
#ifdef __SUPPORT_PS3__
|
||||||
MADR = adr;
|
DMA::MADR = adr;
|
||||||
#else
|
#else
|
||||||
DMA_IO::GPU.set_adr(adr);
|
DMA_IO::GPU.set_adr(adr);
|
||||||
#endif // __SUPPORT_PS3__
|
#endif // __SUPPORT_PS3__
|
||||||
|
@ -101,17 +101,17 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start(uint16_t blockCount, uint16_t wordsPerBlock = 0x10) {
|
static void start(uint16_t blockCount, uint16_t wordsPerBlock = 0x10) {
|
||||||
typedef DMA_IO::BCR::SyncMode1 SyncMode1;
|
using SyncMode1 = DMA_IO::BCR::SyncMode1;
|
||||||
|
|
||||||
#ifdef __SUPPORT_PS3__
|
#ifdef __SUPPORT_PS3__
|
||||||
DMA_IO::GPU.set_adr(MADR);
|
DMA_IO::GPU.set_adr(MADR);
|
||||||
MADR += (blockCount * wordsPerBlock) << 2;
|
DMA::MADR += (blockCount * wordsPerBlock) << 2;
|
||||||
#endif // __SUPPORT_PS3__
|
#endif // __SUPPORT_PS3__
|
||||||
DMA_IO::GPU.block_ctrl.write(DMA_IO::BCR::from(SyncMode1::BlockSize.with(wordsPerBlock), SyncMode1::BlockAmount.with(blockCount)));
|
DMA_IO::GPU.block_ctrl.write(DMA_IO::BCR::from(SyncMode1::BlockSize.with(wordsPerBlock), SyncMode1::BlockAmount.with(blockCount)));
|
||||||
DMA_IO::GPU.channel_ctrl.write(DMA_IO::CHCHR::StartGPUReceive());
|
DMA_IO::GPU.channel_ctrl.write(DMA_IO::CHCHR::StartGPUReceive());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
#include <PSX/System/IOPorts/dma_io.hpp>
|
||||||
|
#include <PSX/SPU/spu.hpp>
|
||||||
|
|
||||||
|
namespace JabyEngine {
|
||||||
|
namespace SPU {
|
||||||
|
namespace internal {
|
||||||
|
struct DMA {
|
||||||
|
static void wait() {
|
||||||
|
DMA_IO::SPU.wait();
|
||||||
|
while(SPU_IO::StatusRegister.read().is_set(SPU_IO_Values::StatusRegister::TransferBusy));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void end() {
|
||||||
|
SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::Stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Receive {
|
||||||
|
static void prepare() {
|
||||||
|
end();
|
||||||
|
SPU_IO::DataTransferControl.write(SPU_IO::DataTransferControl::NormalTransferMode());
|
||||||
|
SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::Stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_src(uintptr_t adr) {
|
||||||
|
DMA_IO::SPU.set_adr(adr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_dst(SPU::SRAMAdr adr) {
|
||||||
|
SPU_IO::SRAMTransferAdr.write(adr);
|
||||||
|
SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::DMAWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(uint16_t blockCount, uint16_t wordsPerBlock = 0x10) {
|
||||||
|
using SyncMode1 = DMA_IO::BCR::SyncMode1;
|
||||||
|
|
||||||
|
DMA_IO::SPU.block_ctrl.write(DMA_IO::BCR::from(SyncMode1::BlockSize.with(wordsPerBlock), SyncMode1::BlockAmount.with(blockCount)));
|
||||||
|
DMA_IO::SPU.channel_ctrl.write(DMA_IO::CHCHR::StartSPUReceive());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#pragma once
|
||||||
|
#include <PSX/jabyengine.hpp>
|
||||||
|
|
||||||
|
namespace JabyEngine {
|
||||||
|
namespace SPU_MMU {
|
||||||
|
// TODO: Make this work with words? Word align?
|
||||||
|
const uint8_t* allocate(uint8_t voice, size_t size);
|
||||||
|
void deallocate(uint8_t voice);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ namespace JabyEngine {
|
||||||
using namespace SPU_IO;
|
using namespace SPU_IO;
|
||||||
|
|
||||||
static void clear_main_volume() {
|
static void clear_main_volume() {
|
||||||
static constexpr auto StartVol = SweepVolume::from(SweepVolume::VolumeEnable, SweepVolume::Volume.with(static_cast<int16_t>(I16_MAX >> 2)));
|
static constexpr auto StartVol = SweepVolume::from(SweepVolume::VolumeMode::Enable, SweepVolume::VolumeMode::Volume.with(SweepVolume::VolumeMode::MaxVolume >> 1));
|
||||||
|
|
||||||
MainVolume::Left.write(StartVol);
|
MainVolume::Left.write(StartVol);
|
||||||
MainVolume::Right.write(StartVol);
|
MainVolume::Right.write(StartVol);
|
||||||
|
@ -18,37 +18,37 @@ namespace JabyEngine {
|
||||||
CDVolume::Left.write(0.75_vol);
|
CDVolume::Left.write(0.75_vol);
|
||||||
CDVolume::Right.write(0.75_vol);
|
CDVolume::Right.write(0.75_vol);
|
||||||
|
|
||||||
ExternalAudioInputVolume::Left.write({0});
|
ExternalAudioInputVolume::Left.write(SimpleVolume::mute());
|
||||||
ExternalAudioInputVolume::Right.write({0});
|
ExternalAudioInputVolume::Right.write(SimpleVolume::mute());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_voice() {
|
static void clear_voice() {
|
||||||
for(auto& voice : SPU_IO::Voice) {
|
for(auto& voice : SPU_IO::Voice) {
|
||||||
voice.volumeLeft.write({0});
|
voice.volumeLeft.write(SweepVolume::mute());
|
||||||
voice.volumeRight.write({0});
|
voice.volumeRight.write(SweepVolume::mute());
|
||||||
voice.sampleRate.write({0});
|
voice.sampleRate.write(SampleRate::stop());
|
||||||
voice.ad.write({0});
|
voice.ad.write(AD::none());
|
||||||
voice.sr.write({0});
|
voice.sr.write(SR::none());
|
||||||
voice.currentVolume.write({0});
|
voice.adsr_volume.write(SimpleVolume::mute());
|
||||||
|
|
||||||
voice.adr.write(Voice::start_adr());
|
voice.adr.write(SRAMAdr::adpcm_start());
|
||||||
voice.repeatAdr.write(Voice::start_adr());
|
voice.repeatAdr.write(SRAMAdr::adpcm_start());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_pmon() {
|
static void clear_pmon() {
|
||||||
SPU_IO::PMON.write({0});
|
SPU_IO::PMON.write(PitchModulation::AllOff());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_noise_and_echo() {
|
static void clear_noise_and_echo() {
|
||||||
SPU_IO::NON.write({0});
|
SPU_IO::NON.write(Noise::AllOff());
|
||||||
SPU_IO::EON.write({0});
|
SPU_IO::EON.write(Echo::AllOff());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_reverb() {
|
static void clear_reverb() {
|
||||||
Reverb::Volume::Left.write({0});
|
Reverb::Volume::Left.write(SimpleVolume::mute());
|
||||||
Reverb::Volume::Right.write({0});
|
Reverb::Volume::Right.write(SimpleVolume::mute());
|
||||||
Reverb::WorkAreaAdr.write({0});
|
Reverb::WorkAreaAdr.write(SRAMAdr::null());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setup_control_register() {
|
static void setup_control_register() {
|
||||||
|
@ -57,25 +57,19 @@ namespace JabyEngine {
|
||||||
SPU_IO::ControlRegister.write(SetupValue);
|
SPU_IO::ControlRegister.write(SetupValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setup_data_transfer_control() {
|
|
||||||
static constexpr struct DataTransferControl RequiredValue{(2 << 1)};
|
|
||||||
|
|
||||||
DataTransferControl.write(RequiredValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wait_voices() {
|
static void wait_voices() {
|
||||||
static constexpr SimpleVolume Treshhold{static_cast<int16_t>(I16_MAX*0.03)};
|
static constexpr auto Treshhold = SimpleVolume::mute();
|
||||||
|
|
||||||
try_again:
|
try_again:
|
||||||
for(const auto& voice : SPU_IO::Voice) {
|
for(const auto& voice : SPU_IO::Voice) {
|
||||||
if(voice.currentVolume.read() > Treshhold) {
|
if(voice.adsr_volume.read() > Treshhold) {
|
||||||
goto try_again;
|
goto try_again;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop_voices() {
|
void stop_voices() {
|
||||||
SPU_IO::Key::Off.write({UI32_MAX});
|
SPU_IO::Key::Off.write(KeyOff::all());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
@ -88,7 +82,6 @@ namespace JabyEngine {
|
||||||
clear_noise_and_echo();
|
clear_noise_and_echo();
|
||||||
clear_reverb();
|
clear_reverb();
|
||||||
|
|
||||||
setup_data_transfer_control();
|
|
||||||
setup_control_register();
|
setup_control_register();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
#include "../../internal-include/GPU/gpu_internal.hpp"
|
#include "../../internal-include/GPU/gpu_internal.hpp"
|
||||||
#include <stdio.hpp>
|
#include <stdio.hpp>
|
||||||
|
|
||||||
|
#include "../../internal-include/SPU/spu_mmu.hpp"
|
||||||
#include <PSX/GTE/gte.hpp>
|
#include <PSX/GTE/gte.hpp>
|
||||||
|
#include <PSX/SPU/spu.hpp>
|
||||||
|
#include <PSX/System/IOPorts/spu_io.hpp>
|
||||||
#include <PSX/System/syscalls.hpp>
|
#include <PSX/System/syscalls.hpp>
|
||||||
|
|
||||||
extern "C" uint32_t __heap_start;
|
extern "C" uint32_t __heap_start;
|
||||||
|
@ -39,6 +42,26 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_spu_alloc() {
|
||||||
|
static const auto calculate_spu_adr = [](size_t size) -> const uint8_t* {
|
||||||
|
return reinterpret_cast<const uint8_t*>(SPU_IO::MemoryMap::ADPCM + size);
|
||||||
|
};
|
||||||
|
static const auto simple_assert = [](uint32_t test_id, const uint8_t* adr, const uint8_t* expected) {
|
||||||
|
static const char* ok_text = "Test %i: 0x%p == 0x%p; OK\n";
|
||||||
|
static const char* failed_text = "Test %i: 0x%p != 0x%p; Failed\n";
|
||||||
|
|
||||||
|
printf(adr == expected ? ok_text : failed_text, test_id, adr, expected);
|
||||||
|
};
|
||||||
|
|
||||||
|
printf("=== SPU test ===\n");
|
||||||
|
simple_assert(0, SPU_MMU::allocate(0, 0x600), calculate_spu_adr(0x0));
|
||||||
|
simple_assert(1, SPU_MMU::allocate(1, 0x800), calculate_spu_adr(0x600));
|
||||||
|
simple_assert(2, SPU_MMU::allocate(0, 0x300), calculate_spu_adr(0x0));
|
||||||
|
simple_assert(3, SPU_MMU::allocate(2, 0x300), calculate_spu_adr(0x300));
|
||||||
|
|
||||||
|
// TODO: More tests
|
||||||
|
}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace Start {
|
namespace Start {
|
||||||
// Thanks to Nicolas Noble!
|
// Thanks to Nicolas Noble!
|
||||||
|
@ -77,6 +100,7 @@ namespace JabyEngine {
|
||||||
GTE::setup();
|
GTE::setup();
|
||||||
test_bios_font();
|
test_bios_font();
|
||||||
test_gte_scale();
|
test_gte_scale();
|
||||||
|
test_spu_alloc();
|
||||||
|
|
||||||
SPU::setup();
|
SPU::setup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,17 @@ namespace JabyEngine {
|
||||||
}(file, buf_cfg, is_lz4);
|
}(file, buf_cfg, is_lz4);
|
||||||
|
|
||||||
switch(file.type) {
|
switch(file.type) {
|
||||||
case CDFileType::SimpleTIM:
|
|
||||||
return FileProcessor::create(data_adr, file.payload.simple_tim);
|
|
||||||
case CDFileType::CopyTo:
|
case CDFileType::CopyTo:
|
||||||
return FileProcessor::create(data_adr, Nothing());
|
return FileProcessor::create(data_adr, Nothing());
|
||||||
|
|
||||||
|
case CDFileType::SimpleTIM:
|
||||||
|
return FileProcessor::create(data_adr, file.payload.simple_tim);
|
||||||
|
|
||||||
|
#ifdef __SUPPORT_VAG__
|
||||||
|
case CDFileType::SonyVAG:
|
||||||
|
return FileProcessor::create(data_adr, file.payload.vag);
|
||||||
|
#endif //__SUPPORT_VAG__
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FileProcessor::create_custom(data_adr, static_cast<CDFileType_t>(file.type) - static_cast<CDFileType_t>(CDFileType::Custom), file.payload);
|
return FileProcessor::create_custom(data_adr, static_cast<CDFileType_t>(file.type) - static_cast<CDFileType_t>(CDFileType::Custom), file.payload);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
State create(const uint32_t* data_adr, const Nothing& nothing) {
|
State create(const uint32_t* data_adr, const Nothing& nothing) {
|
||||||
return State::from(NothingState(), reinterpret_cast<const uint8_t*>(data_adr), parse_nothing);
|
return State::from(NothingState(), data_adr, parse_nothing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
#include "../../../internal-include/GPU/gpu_internal.hpp"
|
#include "../../../internal-include/GPU/gpu_internal.hpp"
|
||||||
#include <PSX/File/file_processor_helper.hpp>
|
#include <PSX/File/file_processor_helper.hpp>
|
||||||
#include <PSX/GPU/gpu_types.hpp>
|
#include <PSX/GPU/gpu_types.hpp>
|
||||||
#include <limits.hpp>
|
|
||||||
#include <stdio.hpp>
|
#include <stdio.hpp>
|
||||||
|
|
||||||
namespace JabyEngine {
|
namespace JabyEngine {
|
||||||
|
@ -63,44 +62,12 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Progress parse_data(State::Configuration& config, SimpleTIMState& state) {
|
static Progress parse_data(State::Configuration& config, SimpleTIMState& state) {
|
||||||
const auto config_data_words = (config.data_bytes/sizeof(uint32_t));
|
const auto [words_to_use, is_last] = Helper::DMA::WordsReady::calculate(config, state.words_left);
|
||||||
const auto words_to_use = (config_data_words > state.words_left) ? state.words_left : config_data_words;
|
const auto words_used = Helper::DMA::send_words<GPU::internal::DMA>(words_to_use, is_last);
|
||||||
bool is_last = (words_to_use == state.words_left);
|
|
||||||
auto block_count = (words_to_use >> 4);
|
|
||||||
|
|
||||||
while(block_count > 0) {
|
state.words_left -= words_used;
|
||||||
const auto block_send = (block_count > UI16_MAX) ? UI16_MAX : block_count;
|
config.processed(words_used*sizeof(uint32_t));
|
||||||
|
return is_last ? Progress::Done : Progress::InProgress;
|
||||||
// Send data!
|
|
||||||
GPU::internal::DMA::wait();
|
|
||||||
GPU::internal::DMA::Receive::start(block_send);
|
|
||||||
block_count -= block_send;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is_last) {
|
|
||||||
// Send words
|
|
||||||
const auto last_words = (words_to_use & 0b1111);
|
|
||||||
if(last_words > 0) {
|
|
||||||
GPU::internal::DMA::wait();
|
|
||||||
GPU::internal::DMA::Receive::start(1, last_words);
|
|
||||||
}
|
|
||||||
|
|
||||||
GPU::internal::DMA::wait();
|
|
||||||
GPU::internal::DMA::end();
|
|
||||||
|
|
||||||
state.words_left = 0;
|
|
||||||
config.processed(words_to_use*sizeof(uint32_t));
|
|
||||||
|
|
||||||
return Progress::Done;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
const auto words_used = (words_to_use & ~0b1111);
|
|
||||||
|
|
||||||
state.words_left -= words_used;
|
|
||||||
config.processed(words_used*sizeof(uint32_t));
|
|
||||||
return Progress::InProgress;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Progress switch_state_parse_data(State::Configuration& config, SimpleTIMState& state) {
|
static Progress switch_state_parse_data(State::Configuration& config, SimpleTIMState& state) {
|
||||||
|
@ -139,7 +106,7 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
State create(const uint32_t* data_adr, const SimpleTIM& file) {
|
State create(const uint32_t* data_adr, const SimpleTIM& file) {
|
||||||
return State::from(SimpleTIMState(file), reinterpret_cast<const uint8_t*>(data_adr), parse_header);
|
return State::from(SimpleTIMState(file), data_adr, parse_header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
#include "../../../internal-include/SPU/spu_internal.hpp"
|
||||||
|
#include <PSX/Auxiliary/big_endian.hpp>
|
||||||
|
#include <PSX/Auxiliary/word_helper.hpp>
|
||||||
|
#include <PSX/File/file_processor_helper.hpp>
|
||||||
|
#include <PSX/SPU/spu.hpp>
|
||||||
|
#include <stdio.hpp>
|
||||||
|
|
||||||
|
#ifdef __SUPPORT_VAG__
|
||||||
|
namespace JabyEngine {
|
||||||
|
namespace FileProcessor {
|
||||||
|
struct VAGHeader {
|
||||||
|
char id[4];
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t reserved;
|
||||||
|
uint32_t sample_size;
|
||||||
|
uint32_t sample_frequency;
|
||||||
|
uint8_t reserved_2[12];
|
||||||
|
char name[16];
|
||||||
|
|
||||||
|
constexpr uint32_t get_version() const {
|
||||||
|
return read_be(this->version);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t get_sample_size() const {
|
||||||
|
return read_be(this->sample_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t get_sample_frequency() const {
|
||||||
|
return read_be(this->sample_frequency);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VAGState {
|
||||||
|
uint32_t voice_id;
|
||||||
|
size_t words_left;
|
||||||
|
SPU::SimpleVolume inital_vol;
|
||||||
|
|
||||||
|
static constexpr VAGState create(uint32_t voice_id, SPU::SimpleVolume inital_vol) {
|
||||||
|
return VAGState{.voice_id = voice_id, .words_left = 0, .inital_vol = inital_vol};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static Progress parse_sample(State::Configuration& config, VAGState& state) {
|
||||||
|
const auto [words_to_use, is_last] = Helper::DMA::WordsReady::calculate(config, state.words_left);
|
||||||
|
const auto words_used = Helper::DMA::send_words<SPU::internal::DMA>(words_to_use, is_last);
|
||||||
|
|
||||||
|
state.words_left -= words_used;
|
||||||
|
config.processed(words_used*sizeof(uint32_t));
|
||||||
|
return is_last ? Progress::Done : Progress::InProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Progress parse_header(State::Configuration& config, VAGState& state) {
|
||||||
|
if(config.data_bytes >= sizeof(VAGHeader)) {
|
||||||
|
const auto& header = *reinterpret_cast<const VAGHeader*>(config.data_adr);
|
||||||
|
const auto words = bytes_to_words(header.get_sample_size());
|
||||||
|
const auto bytes = words_to_bytes(words);
|
||||||
|
|
||||||
|
state.words_left = words;
|
||||||
|
|
||||||
|
auto sram_adr = SPU::voice[state.voice_id].allocate(SPU_IO::SampleRate::from_HZ(header.get_sample_frequency()), bytes);
|
||||||
|
// TODO: Keep this as optional?
|
||||||
|
printf("SPU: Allocated %i @0x%p to 0x%p (%i bytes)\n", state.voice_id, sram_adr.raw, (sram_adr.raw + bytes), bytes);
|
||||||
|
SPU::voice[state.voice_id].set_volume(state.inital_vol, state.inital_vol);
|
||||||
|
|
||||||
|
config.processed(sizeof(VAGHeader));
|
||||||
|
SPU::internal::DMA::Receive::prepare();
|
||||||
|
SPU::internal::DMA::Receive::set_dst(sram_adr);
|
||||||
|
SPU::internal::DMA::Receive::set_src(reinterpret_cast<uintptr_t>(config.data_adr));
|
||||||
|
|
||||||
|
return Helper::exchange_and_execute_process_function(parse_sample, config, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Progress::InProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
State create(const uint32_t* data_adr, const VAG& file) {
|
||||||
|
return State::from(VAGState::create(file.voice_number, file.inital_stereo_vol), data_adr, parse_header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // __SUPPORT_VAG__
|
|
@ -8,11 +8,9 @@ namespace JabyEngine {
|
||||||
uint8_t Display :: current_id = 1; //< Setup will call exchange and set it to 0
|
uint8_t Display :: current_id = 1; //< Setup will call exchange and set it to 0
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
namespace DMA {
|
#ifdef __SUPPORT_PS3__
|
||||||
#ifdef __SUPPORT_PS3__
|
uintptr_t DMA :: MADR = 0;
|
||||||
uintptr_t MADR = 0;
|
#endif // __SUPPORT_PS3__
|
||||||
#endif // __SUPPORT_PS3__
|
|
||||||
}
|
|
||||||
|
|
||||||
static SysCall::InterruptVerifierResult interrupt_verifier();
|
static SysCall::InterruptVerifierResult interrupt_verifier();
|
||||||
static void interrupt_handler(uint32_t);
|
static void interrupt_handler(uint32_t);
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include "../../internal-include/SPU/spu_internal.hpp"
|
||||||
|
#include "../../internal-include/SPU/spu_mmu.hpp"
|
||||||
|
#include <PSX/System/IOPorts/spu_io.hpp>
|
||||||
|
#include <PSX/SPU/spu.hpp>
|
||||||
|
#include <stddef.hpp>
|
||||||
|
|
||||||
|
#include <stdio.hpp>
|
||||||
|
|
||||||
|
namespace JabyEngine {
|
||||||
|
namespace SPU {
|
||||||
|
SRAMAdr Voice :: allocate(size_t size) {
|
||||||
|
Voice::stop();
|
||||||
|
const auto voice_id = Voice::get_id();
|
||||||
|
const auto adr = SRAMAdr{static_cast<SRAMAdr::UnderlyingType>(reinterpret_cast<uintptr_t>(SPU_MMU::allocate(voice_id, size)))};
|
||||||
|
|
||||||
|
SPU_IO::Voice[voice_id].adr.write(adr);
|
||||||
|
return adr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SRAMAdr Voice :: allocate(SPU_IO::SampleRate frequency, size_t size) {
|
||||||
|
const auto result = Voice::allocate(size);
|
||||||
|
Voice::set_sample_rate(frequency);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice :: deallocate() {
|
||||||
|
Voice::stop();
|
||||||
|
SPU_MMU::deallocate(Voice::get_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
#include "../../internal-include/SPU/spu_mmu.hpp"
|
||||||
|
#include <PSX/System/IOPorts/spu_io.hpp>
|
||||||
|
#include <PSX/Auxiliary/math_helper.hpp>
|
||||||
|
#include <PSX/SPU/spu.hpp>
|
||||||
|
#include <stddef.hpp>
|
||||||
|
|
||||||
|
namespace JabyEngine {
|
||||||
|
namespace SPU_MMU {
|
||||||
|
namespace SPU_MemoryMap = SPU_IO::MemoryMap;
|
||||||
|
|
||||||
|
struct SPUMemory {
|
||||||
|
// TODO: v change to uint16_t??
|
||||||
|
const uint8_t* adr;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
static SPUMemory create(size_t size) {
|
||||||
|
return SPUMemory{.adr = reinterpret_cast<const uint8_t*>(SPU_MemoryMap::ADPCM), .size = size};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void clear() {
|
||||||
|
this->adr = nullptr;
|
||||||
|
this->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const uint8_t* get_end_adr() const {
|
||||||
|
return (this->adr + this->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool is_free() const {
|
||||||
|
return this->adr == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool intersects(const SPUMemory& other) const {
|
||||||
|
const auto* min = max_of(this->adr, other.adr);
|
||||||
|
const auto* max = min_of(this->get_end_adr(), other.get_end_adr());
|
||||||
|
return min < max;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AllocatedVoice {
|
||||||
|
SPUMemory memory;
|
||||||
|
AllocatedVoice* next_entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VoiceManager {
|
||||||
|
struct Iterator {
|
||||||
|
AllocatedVoice* *prev_voice;
|
||||||
|
AllocatedVoice* current_voice;
|
||||||
|
|
||||||
|
void next() {
|
||||||
|
this->prev_voice = &this->current_voice->next_entry;
|
||||||
|
this->current_voice = this->current_voice->next_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_next() const {
|
||||||
|
return this->current_voice;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const {
|
||||||
|
return Iterator::has_next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AllocatedVoice allocated_voice_buffer[SPU_IO::VoiceCount + SPU_IO::ReverbCount] = {0};
|
||||||
|
AllocatedVoice* first_allocated_voice = nullptr;
|
||||||
|
|
||||||
|
Iterator iterator() {
|
||||||
|
return Iterator{.prev_voice = &this->first_allocated_voice, .current_voice = this->first_allocated_voice};
|
||||||
|
}
|
||||||
|
|
||||||
|
AllocatedVoice& get_voice(uint8_t id) {
|
||||||
|
return this->allocated_voice_buffer[id];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static VoiceManager voice_mgr;
|
||||||
|
|
||||||
|
using MemoryFoundCallback = const uint8_t* (AllocatedVoice& new_entry, AllocatedVoice* &prev_entry, AllocatedVoice* next_entry);
|
||||||
|
|
||||||
|
static const uint8_t* verify_and_add(AllocatedVoice& new_entry, AllocatedVoice* &prev_entry, AllocatedVoice* next_entry) {
|
||||||
|
// TODO: Verify that we are not crashing into reverb or that we are higher then SPU
|
||||||
|
prev_entry = &new_entry;
|
||||||
|
new_entry.next_entry = next_entry;
|
||||||
|
|
||||||
|
return new_entry.memory.adr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t* find_first_fit(AllocatedVoice &new_entry, MemoryFoundCallback memory_found) {
|
||||||
|
auto iterator = voice_mgr.iterator();
|
||||||
|
while(iterator) {
|
||||||
|
if(!iterator.current_voice->memory.intersects(new_entry.memory)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
new_entry.memory.adr = iterator.current_voice->memory.get_end_adr();
|
||||||
|
iterator.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return memory_found(new_entry, *iterator.prev_voice, iterator.current_voice);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* allocate(uint8_t voice, size_t size) {
|
||||||
|
auto& voice_entry = voice_mgr.get_voice(voice);
|
||||||
|
if(!voice_entry.memory.is_free()) {
|
||||||
|
deallocate(voice);
|
||||||
|
}
|
||||||
|
|
||||||
|
voice_entry.memory = SPUMemory::create(size);
|
||||||
|
return find_first_fit(voice_entry, verify_and_add);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(uint8_t voice) {
|
||||||
|
auto* voice_adr = &voice_mgr.get_voice(voice);
|
||||||
|
auto iterator = voice_mgr.iterator();
|
||||||
|
|
||||||
|
voice_adr->memory.clear();
|
||||||
|
while(iterator) {
|
||||||
|
if(iterator.current_voice == voice_adr) {
|
||||||
|
*iterator.prev_voice = voice_adr->next_entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
iterator.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "jaby_engine_fconv"
|
name = "jaby_engine_fconv"
|
||||||
version = "0.1.6"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
|
@ -1 +1,43 @@
|
||||||
pub mod xa;
|
pub mod xa;
|
||||||
|
pub mod vag;
|
||||||
|
|
||||||
|
use std::{env, path::PathBuf, process::Command};
|
||||||
|
use tool_helper::Error;
|
||||||
|
|
||||||
|
fn run_psxavenc<I, S>(input: PathBuf, output: PathBuf, args: I) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: AsRef<std::ffi::OsStr>, {
|
||||||
|
let psxavenc = get_psxavenc_path()?;
|
||||||
|
let result = Command::new(psxavenc).args(args).arg(input.to_string_lossy().as_ref()).arg(output.to_string_lossy().as_ref()).output()?;
|
||||||
|
|
||||||
|
let stderr = tool_helper::vec_helper::to_string(result.stderr)?;
|
||||||
|
let stdout = tool_helper::vec_helper::to_string(result.stdout)?;
|
||||||
|
|
||||||
|
if !result.status.success() {
|
||||||
|
return Err(Error::from_text(format!("psxavenc returned: {}. {}", result.status, stderr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stdout.is_empty() {
|
||||||
|
println!("{}", stdout);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_psxavenc_path() -> Result<PathBuf, Error> {
|
||||||
|
let tool_path = {
|
||||||
|
let mut my_path = env::current_exe()?;
|
||||||
|
|
||||||
|
my_path.pop();
|
||||||
|
my_path.push("extern");
|
||||||
|
my_path.push("psxavenc");
|
||||||
|
|
||||||
|
my_path
|
||||||
|
};
|
||||||
|
|
||||||
|
if !tool_path.exists() {
|
||||||
|
return Err(Error::from_str("Could not locate psxavenc. Make sure it is installed under \"<Jaby Engine>/bin/extern\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tool_path)
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
use clap::Args;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use tool_helper::Error;
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct Arguments {
|
||||||
|
frequency: Option<u32>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert(args: Arguments, input: PathBuf, output: PathBuf) -> Result<(), Error> {
|
||||||
|
let mut cmd_args = vec!["-t", "vag"];
|
||||||
|
let frequency_str;
|
||||||
|
|
||||||
|
if let Some(frequency) = args.frequency {
|
||||||
|
frequency_str = frequency.to_string().to_owned();
|
||||||
|
|
||||||
|
cmd_args.push("-f");
|
||||||
|
cmd_args.push(frequency_str.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
super::run_psxavenc(input, output, cmd_args)
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use clap::{Args, ValueEnum};
|
use clap::{Args, ValueEnum};
|
||||||
use std::{env, path::PathBuf, process::Command, str};
|
use std::{path::PathBuf, str};
|
||||||
use tool_helper::Error;
|
use tool_helper::Error;
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
|
@ -43,39 +43,13 @@ impl Sample {
|
||||||
pub fn convert(args: Arguments, input: PathBuf, output: PathBuf) -> Result<(), Error> {
|
pub fn convert(args: Arguments, input: PathBuf, output: PathBuf) -> Result<(), Error> {
|
||||||
let quality = args.quality;
|
let quality = args.quality;
|
||||||
let sample = args.sample;
|
let sample = args.sample;
|
||||||
let tool_path = {
|
|
||||||
let mut my_path = env::current_exe()?;
|
super::run_psxavenc(input, output, [
|
||||||
|
|
||||||
my_path.pop();
|
|
||||||
my_path.push("extern");
|
|
||||||
my_path.push("psxavenc");
|
|
||||||
|
|
||||||
my_path
|
|
||||||
};
|
|
||||||
|
|
||||||
if !tool_path.exists() {
|
|
||||||
return Err(Error::from_str("Could not locate psxavenc. Make sure it is installed under \"<Jaby Engine>/bin/extern\""));
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = Command::new(tool_path).args([
|
|
||||||
"-t", "xacd",
|
"-t", "xacd",
|
||||||
"-f", quality.get_frequency().to_string().as_ref(),
|
"-f", quality.get_frequency().to_string().as_ref(),
|
||||||
"-b", "4",
|
"-b", "4",
|
||||||
"-c", sample.get_channel().to_string().as_ref(),
|
"-c", sample.get_channel().to_string().as_ref(),
|
||||||
"-F", "0",
|
"-F", "0",
|
||||||
"-C", "0",
|
"-C", "0",
|
||||||
input.to_string_lossy().as_ref(), output.to_string_lossy().as_ref()
|
])
|
||||||
]) .output()?;
|
|
||||||
|
|
||||||
let stderr = tool_helper::vec_helper::to_string(result.stderr)?;
|
|
||||||
let stdout = tool_helper::vec_helper::to_string(result.stdout)?;
|
|
||||||
|
|
||||||
if !result.status.success() {
|
|
||||||
return Err(Error::from_text(format!("psxavenc returned: {}. {}", result.status, stderr)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !stdout.is_empty() {
|
|
||||||
println!("{}", stdout);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
|
@ -21,9 +21,25 @@ struct CommandLine {
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
enum SubCommands {
|
enum SubCommands {
|
||||||
|
// === Internal Commands ===
|
||||||
Nothing,
|
Nothing,
|
||||||
SimpleTIM(reduced_tim::Arguments),
|
SimpleTIM(reduced_tim::Arguments),
|
||||||
XA(xa::Arguments)
|
|
||||||
|
// === External Commands ===
|
||||||
|
VAG(vag::Arguments),
|
||||||
|
XA(xa::Arguments),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubCommands {
|
||||||
|
pub fn is_external_command(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
SubCommands::Nothing => false,
|
||||||
|
SubCommands::SimpleTIM(_) => false,
|
||||||
|
|
||||||
|
SubCommands::VAG(_) => true,
|
||||||
|
SubCommands::XA(_) => true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_internal_conversion(cmd: CommandLine) -> Result<(), Error> {
|
fn run_internal_conversion(cmd: CommandLine) -> Result<(), Error> {
|
||||||
|
@ -44,7 +60,7 @@ fn run_internal_conversion(cmd: CommandLine) -> Result<(), Error> {
|
||||||
match cmd.sub_command {
|
match cmd.sub_command {
|
||||||
SubCommands::Nothing => nothing::copy(&mut input, dst_buffer),
|
SubCommands::Nothing => nothing::copy(&mut input, dst_buffer),
|
||||||
SubCommands::SimpleTIM(args) => reduced_tim::convert(args, input, dst_buffer),
|
SubCommands::SimpleTIM(args) => reduced_tim::convert(args, input, dst_buffer),
|
||||||
_ => Err(Error::not_implemented("External functions can not be called for internal conversion"))
|
_ => Err(Error::from_str("External functions can not be called for internal conversion"))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,13 +90,14 @@ fn run_external_conversion(cmd: CommandLine) -> Result<(), Error> {
|
||||||
let output_file = cmd.output_file.ok_or(Error::from_str("Output has to be a file"))?;
|
let output_file = cmd.output_file.ok_or(Error::from_str("Output has to be a file"))?;
|
||||||
|
|
||||||
match cmd.sub_command {
|
match cmd.sub_command {
|
||||||
SubCommands::XA(args) => xa::convert(args, input_file, output_file),
|
SubCommands::VAG(args) => vag::convert(args, input_file, output_file),
|
||||||
_ => Err(Error::not_implemented("Internal functions can not be called for external conversion"))
|
SubCommands::XA(args) => xa::convert(args, input_file, output_file),
|
||||||
|
_ => Err(Error::from_str("Internal functions can not be called for external conversion"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_main(cmd: CommandLine) -> Result<(), Error> {
|
fn run_main(cmd: CommandLine) -> Result<(), Error> {
|
||||||
if matches!(cmd.sub_command, SubCommands::XA(_)) {
|
if cmd.sub_command.is_external_command() {
|
||||||
run_external_conversion(cmd)
|
run_external_conversion(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue