mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-26 06:25:16 +00:00
Merge branch 'master' into metal
This commit is contained in:
commit
e6bead147a
22 changed files with 636 additions and 175 deletions
48
.github/issue_template.md
vendored
48
.github/issue_template.md
vendored
|
@ -1,48 +0,0 @@
|
||||||
# EXTREMELY IMPORTANT NOTICE - PLEASE **READ**!!!!!!!
|
|
||||||
|
|
||||||
BY SUBMITTING AN ISSUE, YOU HEREBY AGREE TO COMPLY WITH THESE TERMS.
|
|
||||||
FAILURE TO DO SO MAY RESULT IN YOUR ISSUE BEING DECLARED VOID.
|
|
||||||
|
|
||||||
**ADDITIONALLY, FAILURE TO COMPLY WITH POINTS 1 AND 2 WILL RESULT IN THE INABILITY TO ISSUE FURTHER ISSUE REPORTS.**
|
|
||||||
|
|
||||||
1. this section is exclusively for ISSUES related to Furnace (bugs, major annoyances and others). ONLY THINGS THAT COUNT AS **ISSUES** (ad pedem litterae).
|
|
||||||
2. **THIS SECTION IS NOT FOR SUGGESTIONS, REQUESTS, QUESTIONS, SHOWCASE OR ANY OTHER DISCUSSIONS THAT DO NOT MEET THE CRITERIA AND DEFINITION OF AN __ISSUE__.**
|
|
||||||
- see the Discussions section if you wish to submit these.
|
|
||||||
3. check whether your issue has been reported already.
|
|
||||||
- go to the Issues section, and use the search bar that appears on top of the Issues list.
|
|
||||||
4. include the following information:
|
|
||||||
- version of Furnace (help > about)
|
|
||||||
- operating system (and version)
|
|
||||||
- whether you have downloaded Furnace, or built it from source.
|
|
||||||
5. provide these details if you believe the issue is operating system and/or computer-specific:
|
|
||||||
- CPU model
|
|
||||||
- Windows: go to Control Panel > System.
|
|
||||||
- macOS: go to the Apple menu and select About This Mac...
|
|
||||||
- Linux: use `lscpu` or `cat /proc/cpuinfo`.
|
|
||||||
- graphics card (and driver version)
|
|
||||||
- Windows: open `dxdiag` and observe the Render tab.
|
|
||||||
- macOS: go to the Apple menu and select About This Mac...
|
|
||||||
- this information is not always shown.
|
|
||||||
- this information is not necessary if you use an Apple silicon Mac.
|
|
||||||
- Linux: use `glxinfo | grep OpenGL`.
|
|
||||||
6. if your issue is an abnormal program termination (a "Crash"), you must provide additional details:
|
|
||||||
- the furnace_crash.txt file that is created by Furnace after a Crash. this file is located in the following paths:
|
|
||||||
- Windows: `C:\Users\<username>\furnace_crash.txt`
|
|
||||||
- Linux/other: `/tmp/furnace_crash.txt`
|
|
||||||
- on macOS this file is not generated. you may retrieve information about the Crash by clicking on "Report..." or "Show Details" in the "quit unexpectedly" dialog that appears following the Crash.
|
|
||||||
- make sure to remove any personal information for privacy reasons.
|
|
||||||
- be sure to select "Don't Send" afterwards.
|
|
||||||
- the furnace.log file located in:
|
|
||||||
- Windows: `C:\Users\<username>\AppData\Roaming\furnace\furnace.log`
|
|
||||||
- macOS: `~/Library/Application Support/furnace/furnace.log`
|
|
||||||
- Linux: `~/.config/furnace/furnace.log`
|
|
||||||
- make sure to remove any personal information for privacy reasons.
|
|
||||||
|
|
||||||
BY SUBMITTING AN ISSUE, YOU HEREBY AGREE TO COMPLY WITH THESE TERMS.
|
|
||||||
FAILURE TO DO SO MAY RESULT IN YOUR ISSUE BEING DECLARED VOID.
|
|
||||||
|
|
||||||
**ADDITIONALLY, FAILURE TO COMPLY WITH POINTS 1 AND 2 WILL RESULT IN THE INABILITY TO ISSUE FURTHER ISSUE REPORTS.**
|
|
||||||
|
|
||||||
***END OF NOTICE***
|
|
||||||
PLEASE REMOVE THIS NOTICE AFTER READING.
|
|
||||||
FAILURE TO REMOVE THIS NOTICE IS NEGLIGENCE.
|
|
1
.github/issue_template/config.yml
vendored
Normal file
1
.github/issue_template/config.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
blank_issues_enabled: false
|
33
.github/issue_template/issue.yml
vendored
Normal file
33
.github/issue_template/issue.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
name: Issue Report
|
||||||
|
description: for issues (bugs, annoyances, crashes and similar).
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Suggestions, feature requests, questions, showcase or anything else? Go to the Discussions section.
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: Steps to Reproduce (if applicable)
|
||||||
|
- type: textarea
|
||||||
|
id: info
|
||||||
|
attributes:
|
||||||
|
label: Additional Information
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Furnace version? (help > about)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: This is an issue
|
||||||
|
description: By submitting this issue, you affirm that this is an actual issue (not a suggestion, question or otherwise non-issue).
|
||||||
|
options:
|
||||||
|
- label: I hereby certify that the ticket I am submitting is an issue.
|
||||||
|
required: true
|
1
TODO.md
1
TODO.md
|
@ -1,7 +1,6 @@
|
||||||
# to-do for 0.6.3
|
# to-do for 0.6.3
|
||||||
|
|
||||||
- 9xxx for more chips
|
- 9xxx for more chips
|
||||||
- user presets
|
|
||||||
|
|
||||||
# to-do long term
|
# to-do long term
|
||||||
|
|
||||||
|
|
BIN
demos/nes/infinity.fur
Normal file
BIN
demos/nes/infinity.fur
Normal file
Binary file not shown.
|
@ -15,6 +15,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
|
||||||
- DirectX 11: works with the majority of graphics chips/cards and is optimized specifically for Windows.
|
- DirectX 11: works with the majority of graphics chips/cards and is optimized specifically for Windows.
|
||||||
- SDL Renderer: this was the only available render backend prior to the addition of dedicated OpenGL/DirectX backends in 0.6. default on macOS.
|
- SDL Renderer: this was the only available render backend prior to the addition of dedicated OpenGL/DirectX backends in 0.6. default on macOS.
|
||||||
- it is slower than the other backends.
|
- it is slower than the other backends.
|
||||||
|
- Software: this is a last resort backend which renders the interface in software. very slow!
|
||||||
- **Render driver**: this setting appears when using the SDL Renderer backend. it allows you to select an SDL render driver.
|
- **Render driver**: this setting appears when using the SDL Renderer backend. it allows you to select an SDL render driver.
|
||||||
- **VSync**: synchronizes rendering to VBlank and eliminates tearing.
|
- **VSync**: synchronizes rendering to VBlank and eliminates tearing.
|
||||||
- **Frame rate limit**: allows you to set a frame rate limit (in frames per second).
|
- **Frame rate limit**: allows you to set a frame rate limit (in frames per second).
|
||||||
|
|
62
doc/7-systems/5e01.md
Normal file
62
doc/7-systems/5e01.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# 5E01
|
||||||
|
|
||||||
|
a fantasy sound chip created by Euly, based on the Ricoh 2A03, with some improvements:
|
||||||
|
|
||||||
|
- a 37.5% duty cycle,
|
||||||
|
- 32 noise pitches instead of 16, and
|
||||||
|
- triangle channel becomes a wave channel, with four available waveforms: triangle, saw, sine and square.
|
||||||
|
|
||||||
|
## effects
|
||||||
|
|
||||||
|
- `11xx`: **write to delta modulation counter.** range is `00` to `7F`.
|
||||||
|
- this may be used to attenuate the triangle and noise channels; at `7F`, they will be at about 57% volume.
|
||||||
|
- will not work if a sample is playing.
|
||||||
|
- `12xx`: **set duty cycle/noise mode/waveform of channel.**
|
||||||
|
- may be `0` to `3` for the pulse channels:
|
||||||
|
- `0`: 12.5%
|
||||||
|
- `1`: 25%
|
||||||
|
- `2`: 37.5%
|
||||||
|
- `3`: 50%
|
||||||
|
- may be `0` or `1` for the noise channel:
|
||||||
|
- `0`: long (15-bit LFSR, 32767-step)
|
||||||
|
- `1`: short (9-bit LFSR, 93-step)
|
||||||
|
- may be `0` to `3` for the wave channel:
|
||||||
|
- `0`: triangle
|
||||||
|
- `1`: saw
|
||||||
|
- `2`: square
|
||||||
|
- `3`: sine
|
||||||
|
- `13xy`: **setup sweep up.**
|
||||||
|
- `x` is the time.
|
||||||
|
- `y` is the shift.
|
||||||
|
- set to `0` to disable it.
|
||||||
|
- `14xy`: **setup sweep down.**
|
||||||
|
- `x` is the time.
|
||||||
|
- `y` is the shift.
|
||||||
|
- set to `0` to disable it.
|
||||||
|
- `15xx`: **set envelope mode.**
|
||||||
|
- `0`: envelope + length counter. volume represents envelope duration.
|
||||||
|
- `1`: length counter. volume represents output volume.
|
||||||
|
- `2`: looping envelope. volume represents envelope duration.
|
||||||
|
- `3`: constant volume. default value. volume represents output volume.
|
||||||
|
- pulse and noise channels only.
|
||||||
|
- you may need to apply a phase reset (using the macro) to make the envelope effective.
|
||||||
|
- `16xx`: **set length counter.**
|
||||||
|
- see [NES](nes.md) for more information.
|
||||||
|
- this will trigger phase reset.
|
||||||
|
- `17xx`: **set frame counter mode.**
|
||||||
|
- `0`: 4-step.
|
||||||
|
- `1`: 5-step.
|
||||||
|
- `18xx`: **set PCM channel mode.**
|
||||||
|
- `00`: PCM (software).
|
||||||
|
- `01`: DPCM (hardware).
|
||||||
|
- when in DPCM mode, samples will sound muffled (due to its nature), availables pitches are limited, and loop point is ignored.
|
||||||
|
- `19xx`: **set triangle linear counter.**
|
||||||
|
- `00` to `7F` set the counter.
|
||||||
|
- `80` and higher halt it.
|
||||||
|
- `20xx`: **set DPCM frequency.**
|
||||||
|
- only works in DPCM mode.
|
||||||
|
|
||||||
|
|
||||||
|
## info
|
||||||
|
|
||||||
|
this chip uses the [NES](../4-instrument/nes.md) instrument editor.
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
the console from Nintendo that plays Super Mario Bros. and helped revive the agonizing video game market in the US during mid-80s.
|
the console from Nintendo that plays Super Mario Bros. and helped revive the agonizing video game market in the US during mid-80s.
|
||||||
|
|
||||||
also known as Famicom. it is a five-channel sound generator: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel.
|
also known as Family Computer (Famicom), especially in Japan.
|
||||||
|
|
||||||
|
the console is powered by the Ricoh 2A03, a CPU with sound generator built-in. it has five channels: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel.
|
||||||
|
|
||||||
## effects
|
## effects
|
||||||
|
|
||||||
|
|
|
@ -47,3 +47,10 @@ Furnace achieves the authentic sound of videogame hardware by emulating sound ch
|
||||||
- **YMF262-LLE**: a new core written by the author of the Nuked cores. it features extremely accurate emulation.
|
- **YMF262-LLE**: a new core written by the author of the Nuked cores. it features extremely accurate emulation.
|
||||||
- this core uses even more CPU than YM3812-LLE. not suitable for playback or even rendering if you're impatient!
|
- this core uses even more CPU than YM3812-LLE. not suitable for playback or even rendering if you're impatient!
|
||||||
|
|
||||||
|
- **ESFM core**:
|
||||||
|
- **ESFMu**: the ESFM emulator. best choice but CPU intensive.
|
||||||
|
- **ESFMu (fast)**: this is a modification of ESFMu to reduce CPU usage at the cost of less accuracy.
|
||||||
|
|
||||||
|
- **OPLL core**:
|
||||||
|
- **Nuked-OPLL**: this core is accurate and the default.
|
||||||
|
- **emu2413**: a less accurate core that uses less CPU.
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
// portions apparently taken from FamiTracker source, which is under GPLv2+
|
// portions apparently taken from FamiTracker source, which is under GPLv2+
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - audit for CVEs
|
|
||||||
// - format code?
|
// - format code?
|
||||||
|
|
||||||
#include "fileOpsCommon.h"
|
#include "fileOpsCommon.h"
|
||||||
|
@ -258,6 +257,7 @@ const int eff_conversion_050[][2] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr int ftEffectMapSize = sizeof(ftEffectMap) / sizeof(int);
|
constexpr int ftEffectMapSize = sizeof(ftEffectMap) / sizeof(int);
|
||||||
|
constexpr int eftEffectMapSize = sizeof(eftEffectMap) / sizeof(int);
|
||||||
|
|
||||||
int convertMacros2A03[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY};
|
int convertMacros2A03[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY};
|
||||||
int convertMacrosVRC6[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY};
|
int convertMacrosVRC6[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY};
|
||||||
|
@ -434,7 +434,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
bool hasSequence[256][8];
|
bool hasSequence[256][8];
|
||||||
unsigned char sequenceIndex[256][8];
|
unsigned char sequenceIndex[256][8];
|
||||||
unsigned char macro_types[256][8];
|
unsigned char macro_types[256][8];
|
||||||
std::vector<std::vector<DivInstrumentMacro>> macros;
|
std::vector<DivInstrumentMacro> macros[256];
|
||||||
|
std::vector<String> encounteredBlocks;
|
||||||
unsigned char map_channels[DIV_MAX_CHANS];
|
unsigned char map_channels[DIV_MAX_CHANS];
|
||||||
unsigned int hilightA = 4;
|
unsigned int hilightA = 4;
|
||||||
unsigned int hilightB = 16;
|
unsigned int hilightB = 16;
|
||||||
|
@ -457,13 +458,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
memset(map_channels, 0xfe, DIV_MAX_CHANS * sizeof(unsigned char));
|
memset(map_channels, 0xfe, DIV_MAX_CHANS * sizeof(unsigned char));
|
||||||
|
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
std::vector<DivInstrumentMacro> mac;
|
|
||||||
|
|
||||||
for (int j = 0; j < 8; j++) {
|
for (int j = 0; j < 8; j++) {
|
||||||
mac.push_back(DivInstrumentMacro(DIV_MACRO_VOL));
|
macros[i].push_back(DivInstrumentMacro(DIV_MACRO_VOL));
|
||||||
}
|
}
|
||||||
|
|
||||||
macros.push_back(mac);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reader.seek((dnft && dnft_sig) ? 21 : 18, SEEK_SET)) {
|
if (!reader.seek((dnft && dnft_sig) ? 21 : 18, SEEK_SET)) {
|
||||||
|
@ -482,7 +479,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (DivSubSong* i : ds.subsong) {
|
for (DivSubSong* i: ds.subsong) {
|
||||||
i->clearData();
|
i->clearData();
|
||||||
delete i;
|
delete i;
|
||||||
}
|
}
|
||||||
|
@ -501,13 +498,30 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
|
|
||||||
// not the end
|
// not the end
|
||||||
reader.seek(-3, SEEK_CUR);
|
if (!reader.seek(-3, SEEK_CUR)) {
|
||||||
|
logE("couldn't seek back by 3!");
|
||||||
|
lastError = "couldn't seek back by 3";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
blockName = reader.readString(16);
|
blockName = reader.readString(16);
|
||||||
unsigned int blockVersion = (unsigned int)reader.readI();
|
unsigned int blockVersion = (unsigned int)reader.readI();
|
||||||
unsigned int blockSize = (unsigned int)reader.readI();
|
unsigned int blockSize = (unsigned int)reader.readI();
|
||||||
size_t blockStart = reader.tell();
|
size_t blockStart = reader.tell();
|
||||||
|
|
||||||
logD("reading block %s (version %d, %d bytes, position %x)", blockName, blockVersion, blockSize, reader.tell());
|
logD("reading block %s (version %d, %d bytes, position %x)", blockName, blockVersion, blockSize, reader.tell());
|
||||||
|
|
||||||
|
for (String& i: encounteredBlocks) {
|
||||||
|
if (blockName==i) {
|
||||||
|
logE("duplicate block %s!",blockName);
|
||||||
|
lastError = "duplicate block "+blockName;
|
||||||
|
ds.unload();
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encounteredBlocks.push_back(blockName);
|
||||||
|
|
||||||
if (blockName == "PARAMS") {
|
if (blockName == "PARAMS") {
|
||||||
// versions 7-9 don't change anything?
|
// versions 7-9 don't change anything?
|
||||||
CHECK_BLOCK_VERSION(9);
|
CHECK_BLOCK_VERSION(9);
|
||||||
|
@ -529,6 +543,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
|
|
||||||
tchans = reader.readI();
|
tchans = reader.readI();
|
||||||
|
|
||||||
|
if (tchans<0 || tchans>=DIV_MAX_CHANS) {
|
||||||
|
logE("invalid channel count! %d",tchans);
|
||||||
|
lastError = "invalid channel count";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (tchans == 5) {
|
if (tchans == 5) {
|
||||||
expansions = 0; // This is strange. Sometimes expansion chip is set to 0xFF in files
|
expansions = 0; // This is strange. Sometimes expansion chip is set to 0xFF in files
|
||||||
}
|
}
|
||||||
|
@ -538,12 +559,15 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
if (blockVersion >= 7) {
|
if (blockVersion >= 7) {
|
||||||
// advanced Hz control
|
// advanced Hz control
|
||||||
int controlType = reader.readI();
|
int controlType = reader.readI();
|
||||||
switch (controlType) {
|
int readHz=reader.readI();
|
||||||
|
if (readHz<=0) {
|
||||||
|
customHz=60.0;
|
||||||
|
} else switch (controlType) {
|
||||||
case 1:
|
case 1:
|
||||||
customHz = 1000000.0 / (double)reader.readI();
|
customHz = 1000000.0 / (double)readHz;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
reader.readI();
|
logW("unsupported tick rate control type %d",controlType);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -553,6 +577,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
customHz = reader.readI();
|
customHz = reader.readI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logV("before clamp: %f",customHz);
|
||||||
|
|
||||||
|
if (customHz>1000.0) customHz=1000.0;
|
||||||
|
|
||||||
unsigned int newVibrato = 0;
|
unsigned int newVibrato = 0;
|
||||||
bool sweepReset = false;
|
bool sweepReset = false;
|
||||||
unsigned int speedSplitPoint = 0;
|
unsigned int speedSplitPoint = 0;
|
||||||
|
@ -576,6 +604,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
|
|
||||||
if ((expansions & 16) && blockVersion >= 5) { // N163 channels
|
if ((expansions & 16) && blockVersion >= 5) { // N163 channels
|
||||||
n163Chans = reader.readI();
|
n163Chans = reader.readI();
|
||||||
|
if (n163Chans<1 || n163Chans>=9) {
|
||||||
|
logE("invalid Namco 163 channel count! %d",n163Chans);
|
||||||
|
lastError = "invalid Namco 163 channel count";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (blockVersion >= 6) {
|
if (blockVersion >= 6) {
|
||||||
speedSplitPoint = reader.readI();
|
speedSplitPoint = reader.readI();
|
||||||
|
@ -779,6 +813,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (calcChans != tchans) {
|
if (calcChans != tchans) {
|
||||||
|
// TODO: would ignore trigger CVE? too bad if so!
|
||||||
if (!eft || (eft && (expansions & 8) == 0)) // ignore since I have no idea how to tell apart E-FT versions which do or do not have PCM chan. Yes, this may lead to all the righer channels to be shifted but at least you still get note data!
|
if (!eft || (eft && (expansions & 8) == 0)) // ignore since I have no idea how to tell apart E-FT versions which do or do not have PCM chan. Yes, this may lead to all the righer channels to be shifted but at least you still get note data!
|
||||||
{
|
{
|
||||||
logE("channel counts do not match! %d != %d", tchans, calcChans);
|
logE("channel counts do not match! %d != %d", tchans, calcChans);
|
||||||
|
@ -788,17 +823,20 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tchans > DIV_MAX_CHANS) {
|
if (tchans > DIV_MAX_CHANS) {
|
||||||
tchans = DIV_MAX_CHANS;
|
logE("too many channels!");
|
||||||
logW("too many channels!");
|
lastError = "too many channels";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (blockVersion == 9 && blockSize - (reader.tell() - blockStart) == 2) // weird
|
if (blockVersion == 9 && blockSize - (reader.tell() - blockStart) == 2) // weird
|
||||||
{
|
{
|
||||||
reader.seek(2, SEEK_CUR);
|
if (!reader.seek(2, SEEK_CUR)) {
|
||||||
|
logE("could not weird-seek by 2!");
|
||||||
|
lastError = "could not weird-seek by 2";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (eft) {
|
|
||||||
// reader.seek(8,SEEK_CUR);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (blockName == "INFO") {
|
} else if (blockName == "INFO") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
ds.name = reader.readString(32);
|
ds.name = reader.readString(32);
|
||||||
|
@ -831,6 +869,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
for (int j = 0; j <= totalSongs; j++) {
|
for (int j = 0; j <= totalSongs; j++) {
|
||||||
unsigned char effectCols = reader.readC();
|
unsigned char effectCols = reader.readC();
|
||||||
|
|
||||||
|
if (effectCols>7) {
|
||||||
|
logE("too many effect columns!");
|
||||||
|
lastError = "too many effect columns";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (map_channels[i] == 0xfe) {
|
if (map_channels[i] == 0xfe) {
|
||||||
ds.subsong[j]->pat[i].effectCols = 1;
|
ds.subsong[j]->pat[i].effectCols = 1;
|
||||||
logV("- song %d has %d effect columns", j, effectCols);
|
logV("- song %d has %d effect columns", j, effectCols);
|
||||||
|
@ -850,8 +895,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
} else if (blockName == "INSTRUMENTS") {
|
} else if (blockName == "INSTRUMENTS") {
|
||||||
CHECK_BLOCK_VERSION(9);
|
CHECK_BLOCK_VERSION(9);
|
||||||
|
|
||||||
// reader.seek(blockSize,SEEK_CUR);
|
|
||||||
|
|
||||||
ds.insLen = reader.readI();
|
ds.insLen = reader.readI();
|
||||||
if (ds.insLen < 0 || ds.insLen > 256) {
|
if (ds.insLen < 0 || ds.insLen > 256) {
|
||||||
logE("too many instruments/out of range!");
|
logE("too many instruments/out of range!");
|
||||||
|
@ -870,10 +913,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
for (int i = 0; i < ds.insLen; i++) {
|
for (int i = 0; i < ds.insLen; i++) {
|
||||||
unsigned int insIndex = reader.readI();
|
unsigned int insIndex = reader.readI();
|
||||||
if (insIndex >= ds.ins.size()) {
|
if (insIndex >= ds.ins.size()) {
|
||||||
// logE("instrument index %d is out of range!",insIndex);
|
logE("instrument index %d is out of range!",insIndex);
|
||||||
// lastError="instrument index out of range";
|
lastError="instrument index out of range";
|
||||||
// delete[] file;
|
delete[] file;
|
||||||
// return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DivInstrument* ins = ds.ins[insIndex];
|
DivInstrument* ins = ds.ins[insIndex];
|
||||||
|
@ -937,6 +980,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
if (blockVersion >= 7) {
|
if (blockVersion >= 7) {
|
||||||
note = reader.readC();
|
note = reader.readC();
|
||||||
}
|
}
|
||||||
|
if (note<0 || note>=120) {
|
||||||
|
logE("DPCM note %d out of range!",note);
|
||||||
|
lastError = "DPCM note out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
ins->amiga.noteMap[note].map = (short)((unsigned char)reader.readC()) - 1;
|
ins->amiga.noteMap[note].map = (short)((unsigned char)reader.readC()) - 1;
|
||||||
unsigned char freq = reader.readC();
|
unsigned char freq = reader.readC();
|
||||||
ins->amiga.noteMap[note].dpcmFreq = (freq & 15); // 0-15 = 0-15 unlooped, 128-143 = 0-15 looped
|
ins->amiga.noteMap[note].dpcmFreq = (freq & 15); // 0-15 = 0-15 unlooped, 128-143 = 0-15 looped
|
||||||
|
@ -981,8 +1030,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
case DIV_INS_OPLL: {
|
case DIV_INS_OPLL: {
|
||||||
ins->fm.opllPreset = (unsigned int)reader.readI();
|
ins->fm.opllPreset = (unsigned int)reader.readI();
|
||||||
|
ins->fm.opllPreset&=15;
|
||||||
|
|
||||||
unsigned char custom_patch[8] = {0};
|
unsigned char custom_patch[8];
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
custom_patch[i] = reader.readC();
|
custom_patch[i] = reader.readC();
|
||||||
|
@ -1020,24 +1070,35 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
for (int j = 0; j < 64; j++) {
|
for (int j = 0; j < 64; j++) {
|
||||||
wave->data[j] = reader.readC();
|
wave->data[j] = reader.readC();
|
||||||
}
|
}
|
||||||
ins->std.waveMacro.len = 1;
|
|
||||||
ins->std.waveMacro.val[0] = ds.wave.size();
|
|
||||||
for (int j = 0; j < 32; j++) {
|
for (int j = 0; j < 32; j++) {
|
||||||
ins->fds.modTable[j] = reader.readC() - 3;
|
ins->fds.modTable[j] = reader.readC() - 3;
|
||||||
}
|
}
|
||||||
ins->fds.modSpeed = reader.readI();
|
ins->fds.modSpeed = reader.readI();
|
||||||
ins->fds.modDepth = reader.readI();
|
ins->fds.modDepth = reader.readI();
|
||||||
reader.readI(); // this is delay. currently ignored. TODO.
|
reader.readI(); // this is delay. currently ignored. TODO.
|
||||||
ds.wave.push_back(wave);
|
if (ds.wave.size()>=256) {
|
||||||
ds.waveLen++;
|
logW("too many waves! ignoring...");
|
||||||
|
delete wave;
|
||||||
|
} else {
|
||||||
|
ins->std.waveMacro.len = 1;
|
||||||
|
ins->std.waveMacro.val[0] = ds.wave.size();
|
||||||
|
ds.wave.push_back(wave);
|
||||||
|
ds.waveLen++;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int a = reader.readI();
|
unsigned int a = reader.readI();
|
||||||
unsigned int b = reader.readI();
|
unsigned int b = reader.readI();
|
||||||
|
|
||||||
reader.seek(-8, SEEK_CUR);
|
if (!reader.seek(-8, SEEK_CUR)) {
|
||||||
|
logE("couldn't seek back by 8 reading FDS ins");
|
||||||
|
lastError = "couldn't seek back by 8 reading FDS ins";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (a < 256 && (b & 0xFF) != 0x00) {
|
if (a < 256 && (b & 0xFF) != 0x00) {
|
||||||
// don't look at me like this. I don't know why this should be like this either!
|
// don't look at me like this. I don't know why this should be like this either!
|
||||||
|
logW("a is less than 256 and b is not zero!");
|
||||||
} else {
|
} else {
|
||||||
ins->std.volMacro.len = reader.readC();
|
ins->std.volMacro.len = reader.readC();
|
||||||
ins->std.volMacro.loop = reader.readI();
|
ins->std.volMacro.loop = reader.readI();
|
||||||
|
@ -1112,12 +1173,19 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
|
|
||||||
if (blockVersion >= 8) {
|
if (blockVersion >= 8) {
|
||||||
unsigned int autopos = reader.readI();
|
unsigned int autopos = reader.readI();
|
||||||
(void)autopos;
|
logV("autopos: %d",autopos);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int wave_count = reader.readI();
|
unsigned int wave_count = reader.readI();
|
||||||
size_t waveOff = ds.wave.size();
|
size_t waveOff = ds.wave.size();
|
||||||
|
|
||||||
|
if (wave_size>256) {
|
||||||
|
logE("wave size %d out of range",wave_size);
|
||||||
|
lastError = "wave size out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned int ii = 0; ii < wave_count; ii++) {
|
for (unsigned int ii = 0; ii < wave_count; ii++) {
|
||||||
DivWavetable* wave = new DivWavetable();
|
DivWavetable* wave = new DivWavetable();
|
||||||
wave->len = wave_size;
|
wave->len = wave_size;
|
||||||
|
@ -1131,6 +1199,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
if (ds.wave.size()<256) {
|
if (ds.wave.size()<256) {
|
||||||
ds.wave.push_back(wave);
|
ds.wave.push_back(wave);
|
||||||
} else {
|
} else {
|
||||||
|
logW("too many waves...");
|
||||||
delete wave;
|
delete wave;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1296,16 +1365,30 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instVersion == 2) {
|
if (instVersion == 2) {
|
||||||
reader.seek(seek_amount, SEEK_CUR); // what the fuck
|
// I know right?
|
||||||
|
if (!reader.seek(seek_amount, SEEK_CUR)) {
|
||||||
|
logE("EFT seek fail");
|
||||||
|
lastError = "EFT seek fail";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this commented out block left here intentionally.
|
||||||
|
// total mess of code style... for with no space, UNDEFINED CHAR, escaping the unescapable, silly var names...
|
||||||
|
// ...whatever.
|
||||||
/*for(int tti = 0; tti < 20; tti++)
|
/*for(int tti = 0; tti < 20; tti++)
|
||||||
{
|
{
|
||||||
char aaaa = reader.readC();
|
char aaaa = reader.readC();
|
||||||
logV("\'%c\'", aaaa);
|
logV("\'%c\'", aaaa);
|
||||||
}*/
|
}*/
|
||||||
} else {
|
} else {
|
||||||
reader.seek(-4, SEEK_CUR);
|
if (!reader.seek(-4, SEEK_CUR)) {
|
||||||
|
logE("EFT -4 seek fail");
|
||||||
|
lastError = "EFT -4 seek fail";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1325,7 +1408,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.insLen = 128;
|
ds.insLen = 128;
|
||||||
|
|
||||||
} else if (blockName == "SEQUENCES") {
|
} else if (blockName == "SEQUENCES") {
|
||||||
CHECK_BLOCK_VERSION(6);
|
CHECK_BLOCK_VERSION(6);
|
||||||
|
|
||||||
|
@ -1342,6 +1424,14 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
unsigned int index = reader.readI();
|
unsigned int index = reader.readI();
|
||||||
unsigned int type = reader.readI();
|
unsigned int type = reader.readI();
|
||||||
unsigned char size = reader.readC();
|
unsigned char size = reader.readC();
|
||||||
|
|
||||||
|
if (index>=256 || type>=8) {
|
||||||
|
logE("%d: index/type out of range",i);
|
||||||
|
lastError = "sequence index/type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
macros[index][type].len = size;
|
macros[index][type].len = size;
|
||||||
|
|
||||||
for (int j = 0; j < size; j++) {
|
for (int j = 0; j < size; j++) {
|
||||||
|
@ -1361,12 +1451,34 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
unsigned char* Indices = new unsigned char[128 * 5];
|
unsigned char* Indices = new unsigned char[128 * 5];
|
||||||
unsigned char* Types = new unsigned char[128 * 5];
|
unsigned char* Types = new unsigned char[128 * 5];
|
||||||
|
|
||||||
|
memset(Indices,0,128*5);
|
||||||
|
memset(Types,0,128*5);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < seq_count; i++) {
|
for (unsigned int i = 0; i < seq_count; i++) {
|
||||||
unsigned int index = reader.readI();
|
unsigned int index = reader.readI();
|
||||||
|
if (index>=128*5) {
|
||||||
|
logE("%d: index out of range",i);
|
||||||
|
lastError = "sequence index out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Indices[i] = index;
|
Indices[i] = index;
|
||||||
unsigned int type = reader.readI();
|
unsigned int type = reader.readI();
|
||||||
|
if (type>=128*5) {
|
||||||
|
logE("%d: type out of range",i);
|
||||||
|
lastError = "sequence type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Types[i] = type;
|
Types[i] = type;
|
||||||
|
|
||||||
|
if (index>=256 || type>=8) {
|
||||||
|
logE("%d: index/type out of range",i);
|
||||||
|
lastError = "sequence index/type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char size = reader.readC();
|
unsigned char size = reader.readC();
|
||||||
unsigned int setting = 0;
|
unsigned int setting = 0;
|
||||||
|
|
||||||
|
@ -1393,7 +1505,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
DivInstrument* ins = ds.ins[k];
|
DivInstrument* ins = ds.ins[k];
|
||||||
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) {
|
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) {
|
||||||
copyMacro(ins, ¯os[index][type], Types[i], setting);
|
copyMacro(ins, ¯os[index][type], Types[i], setting);
|
||||||
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1412,7 +1523,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
macro_types[k][j] = setting;
|
macro_types[k][j] = setting;
|
||||||
|
|
||||||
copyMacro(ins, ¯os[sequenceIndex[k][j]][j], j, setting);
|
copyMacro(ins, ¯os[sequenceIndex[k][j]][j], j, setting);
|
||||||
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)j, true), ¯os[sequenceIndex[k][j]][j], sizeof(DivInstrumentMacro));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1425,9 +1535,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
unsigned int release = reader.readI();
|
unsigned int release = reader.readI();
|
||||||
unsigned int setting = reader.readI();
|
unsigned int setting = reader.readI();
|
||||||
|
|
||||||
// macros[index][type].rel = release;
|
|
||||||
// macro_types[index][type] = setting;
|
|
||||||
|
|
||||||
for (int k = 0; k < (int)ds.ins.size(); k++) {
|
for (int k = 0; k < (int)ds.ins.size(); k++) {
|
||||||
DivInstrument* ins = ds.ins[k];
|
DivInstrument* ins = ds.ins[k];
|
||||||
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) {
|
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) {
|
||||||
|
@ -1435,7 +1542,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
macro_types[k][Types[i]] = setting;
|
macro_types[k][Types[i]] = setting;
|
||||||
|
|
||||||
copyMacro(ins, ¯os[sequenceIndex[k][Types[i]]][Types[i]], Types[i], setting);
|
copyMacro(ins, ¯os[sequenceIndex[k][Types[i]]][Types[i]], Types[i], setting);
|
||||||
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[k][Types[i]]][Types[i]], sizeof(DivInstrumentMacro));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1446,12 +1552,11 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
} else if (blockName == "GROOVES") {
|
} else if (blockName == "GROOVES") {
|
||||||
CHECK_BLOCK_VERSION(6);
|
CHECK_BLOCK_VERSION(6);
|
||||||
// reader.seek(blockSize,SEEK_CUR);
|
|
||||||
|
|
||||||
unsigned char num_grooves = reader.readC();
|
unsigned char num_grooves = reader.readC();
|
||||||
int max_groove = 0;
|
int max_groove = 0;
|
||||||
|
|
||||||
for (int i = 0; i < 0xff; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
ds.grooves.push_back(DivGroovePattern());
|
ds.grooves.push_back(DivGroovePattern());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1459,15 +1564,18 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
unsigned char index = reader.readC();
|
unsigned char index = reader.readC();
|
||||||
unsigned char size = reader.readC();
|
unsigned char size = reader.readC();
|
||||||
|
|
||||||
if (index > max_groove)
|
if (index > max_groove) {
|
||||||
max_groove = index + 1;
|
max_groove = index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
DivGroovePattern gp;
|
DivGroovePattern gp;
|
||||||
gp.len = size;
|
gp.len = size;
|
||||||
|
|
||||||
for (int sz = 0; sz < size; sz++) {
|
for (int sz = 0; sz < size; sz++) {
|
||||||
unsigned char value = reader.readC();
|
unsigned char value = reader.readC();
|
||||||
gp.val[sz] = value;
|
if (sz<16) {
|
||||||
|
gp.val[sz] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.grooves[index] = gp;
|
ds.grooves[index] = gp;
|
||||||
|
@ -1488,14 +1596,21 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
if ((reader.tell() - blockStart) != blockSize) {
|
if ((reader.tell() - blockStart) != blockSize) {
|
||||||
logE("block %s size does not match! block size %d curr pos %d", blockName, blockSize, reader.tell() - blockStart);
|
logE("block %s size does not match! block size %d curr pos %d", blockName, blockSize, reader.tell() - blockStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (blockName == "FRAMES") {
|
} else if (blockName == "FRAMES") {
|
||||||
CHECK_BLOCK_VERSION(3);
|
CHECK_BLOCK_VERSION(3);
|
||||||
|
|
||||||
for (size_t i = 0; i < ds.subsong.size(); i++) {
|
for (size_t i = 0; i < ds.subsong.size(); i++) {
|
||||||
DivSubSong* s = ds.subsong[i];
|
DivSubSong* s = ds.subsong[i];
|
||||||
|
|
||||||
s->ordersLen = reader.readI();
|
int framesLen=reader.readI();
|
||||||
|
if (framesLen<=1 || framesLen>256) {
|
||||||
|
logE("frames out of range");
|
||||||
|
lastError = "frames out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->ordersLen = framesLen;
|
||||||
if (blockVersion >= 3) {
|
if (blockVersion >= 3) {
|
||||||
s->speeds.val[0] = reader.readI();
|
s->speeds.val[0] = reader.readI();
|
||||||
}
|
}
|
||||||
|
@ -1510,18 +1625,31 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
s->virtualTempoN = tempo;
|
s->virtualTempoN = tempo;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->patLen = reader.readI();
|
int patLen=reader.readI();
|
||||||
|
if (patLen<1 || patLen>256) {
|
||||||
|
logE("pattern length out of range");
|
||||||
|
lastError = "pattern length out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
s->patLen = patLen;
|
||||||
}
|
}
|
||||||
int why = tchans;
|
int why = tchans;
|
||||||
if (blockVersion == 1) {
|
if (blockVersion == 1) {
|
||||||
why = reader.readI();
|
why = reader.readI();
|
||||||
|
if (why<0 || why>=DIV_MAX_CHANS) {
|
||||||
|
logE("why out of range!");
|
||||||
|
lastError = "why out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logV("reading %d and %d orders", tchans, s->ordersLen);
|
logV("reading %d and %d orders", tchans, s->ordersLen);
|
||||||
|
|
||||||
for (int j = 0; j < s->ordersLen; j++) {
|
for (int j = 0; j < s->ordersLen; j++) {
|
||||||
for (int k = 0; k < why; k++) {
|
for (int k = 0; k < why; k++) {
|
||||||
unsigned char o = reader.readC();
|
unsigned char o = reader.readC();
|
||||||
// logV("%.2x",o);
|
if (map_channels[k]>=DIV_MAX_CHANS) continue;
|
||||||
s->orders.ord[map_channels[k]][j] = o;
|
s->orders.ord[map_channels[k]][j] = o;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1533,6 +1661,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
|
|
||||||
if (blockVersion == 1) {
|
if (blockVersion == 1) {
|
||||||
int patLenOld = reader.readI();
|
int patLenOld = reader.readI();
|
||||||
|
if (patLenOld<1 || patLenOld>=256) {
|
||||||
|
logE("old pattern length out of range");
|
||||||
|
lastError = "old pattern length out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
for (DivSubSong* i : ds.subsong) {
|
for (DivSubSong* i : ds.subsong) {
|
||||||
i->patLen = patLenOld;
|
i->patLen = patLenOld;
|
||||||
}
|
}
|
||||||
|
@ -1553,6 +1687,37 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
logV("patNum: %d",patNum);
|
logV("patNum: %d",patNum);
|
||||||
logV("rows: %d",numRows);
|
logV("rows: %d",numRows);
|
||||||
|
|
||||||
|
if (subs<0 || subs>=(int)ds.subsong.size()) {
|
||||||
|
logE("subsong out of range!");
|
||||||
|
lastError = "subsong out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ch<0 || ch>=DIV_MAX_CHANS) {
|
||||||
|
logE("channel out of range!");
|
||||||
|
lastError = "channel out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (map_channels[ch]>=DIV_MAX_CHANS) {
|
||||||
|
logE("mapped channel out of range!");
|
||||||
|
lastError = "mapped channel out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (patNum<0 || patNum>=256) {
|
||||||
|
logE("pattern number out of range!");
|
||||||
|
lastError = "pattern number out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (numRows<0) {
|
||||||
|
logE("row count is negative!");
|
||||||
|
lastError = "row count is negative";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
DivPattern* pat = ds.subsong[subs]->pat[map_channels[ch]].getPattern(patNum, true);
|
DivPattern* pat = ds.subsong[subs]->pat[map_channels[ch]].getPattern(patNum, true);
|
||||||
for (int i = 0; i < numRows; i++) {
|
for (int i = 0; i < numRows; i++) {
|
||||||
unsigned int row = 0;
|
unsigned int row = 0;
|
||||||
|
@ -1562,6 +1727,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
row = reader.readI();
|
row = reader.readI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (row>=256) {
|
||||||
|
logE("row index out of range");
|
||||||
|
lastError = "row index out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char nextNote = reader.readC();
|
unsigned char nextNote = reader.readC();
|
||||||
unsigned char nextOctave = reader.readC();
|
unsigned char nextOctave = reader.readC();
|
||||||
|
|
||||||
|
@ -1592,6 +1764,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char nextIns = reader.readC();
|
unsigned char nextIns = reader.readC();
|
||||||
|
// TODO: you sure about 0xff?
|
||||||
if (map_channels[ch] != 0xff) {
|
if (map_channels[ch] != 0xff) {
|
||||||
if (nextIns < 0x40 && nextNote != 0x0d && nextNote != 0x0e) {
|
if (nextIns < 0x40 && nextNote != 0x0d && nextNote != 0x0e) {
|
||||||
pat->data[row][2] = nextIns;
|
pat->data[row][2] = nextIns;
|
||||||
|
@ -1606,6 +1779,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
pat->data[row][3] = nextVol;
|
pat->data[row][3] = nextVol;
|
||||||
if (map_channels[ch] == vrc6_saw_chan) // scale volume
|
if (map_channels[ch] == vrc6_saw_chan) // scale volume
|
||||||
{
|
{
|
||||||
|
// TODO: shouldn't it be 32?
|
||||||
pat->data[row][3] = (pat->data[row][3] * 42) / 15;
|
pat->data[row][3] = (pat->data[row][3] * 42) / 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1625,13 +1799,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
effectCols = 1;
|
effectCols = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
logV("effectCols: %d",effectCols);
|
|
||||||
|
|
||||||
unsigned char nextEffectVal = 0;
|
unsigned char nextEffectVal = 0;
|
||||||
unsigned char nextEffect = 0;
|
unsigned char nextEffect = 0;
|
||||||
|
|
||||||
//logV("row %d effects are read at %x",row,reader.tell());
|
|
||||||
|
|
||||||
for (int j = 0; j < effectCols; j++) {
|
for (int j = 0; j < effectCols; j++) {
|
||||||
nextEffect = reader.readC();
|
nextEffect = reader.readC();
|
||||||
|
|
||||||
|
@ -1727,14 +1897,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// logW("next effect %d val %d", nextEffect, nextEffectVal);
|
|
||||||
|
|
||||||
if (map_channels[ch] != 0xff) {
|
if (map_channels[ch] != 0xff) {
|
||||||
if (nextEffect == 0 && nextEffectVal == 0) {
|
if (nextEffect == 0 && nextEffectVal == 0) {
|
||||||
pat->data[row][4 + (j * 2)] = -1;
|
pat->data[row][4 + (j * 2)] = -1;
|
||||||
pat->data[row][5 + (j * 2)] = -1;
|
pat->data[row][5 + (j * 2)] = -1;
|
||||||
} else {
|
} else {
|
||||||
if (nextEffect < ftEffectMapSize) {
|
if ((eft && nextEffect<eftEffectMapSize) || (!eft && nextEffect<ftEffectMapSize)) {
|
||||||
if (eft) {
|
if (eft) {
|
||||||
pat->data[row][4 + (j * 2)] = eftEffectMap[nextEffect];
|
pat->data[row][4 + (j * 2)] = eftEffectMap[nextEffect];
|
||||||
pat->data[row][5 + (j * 2)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal;
|
pat->data[row][5 + (j * 2)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal;
|
||||||
|
@ -1782,7 +1950,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
} else if (blockName == "DPCM SAMPLES") {
|
} else if (blockName == "DPCM SAMPLES") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
// reader.seek(blockSize,SEEK_CUR);
|
|
||||||
unsigned char num_samples = reader.readC();
|
unsigned char num_samples = reader.readC();
|
||||||
|
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
|
@ -1808,14 +1975,16 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
|
|
||||||
unsigned int sample_len = reader.readI();
|
unsigned int sample_len = reader.readI();
|
||||||
|
|
||||||
|
if (sample_len>=2097152) {
|
||||||
|
logE("%d: sample too large! %d",index,sample_len);
|
||||||
|
lastError = "sample too large";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
true_size = sample_len + ((1 - (int)sample_len) & 0x0f);
|
true_size = sample_len + ((1 - (int)sample_len) & 0x0f);
|
||||||
sample->lengthDPCM = true_size;
|
sample->init(true_size * 8);
|
||||||
sample->samples = true_size * 8;
|
|
||||||
|
|
||||||
sample->dataDPCM = new unsigned char[true_size];
|
|
||||||
|
|
||||||
memset(sample->dataDPCM, 0xAA, true_size);
|
memset(sample->dataDPCM, 0xAA, true_size);
|
||||||
|
|
||||||
reader.read(sample->dataDPCM, sample_len);
|
reader.read(sample->dataDPCM, sample_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1824,7 +1993,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
for (int i = 255; i > 0; i--) {
|
for (int i = 255; i > 0; i--) {
|
||||||
DivSample* s = ds.sample[i];
|
DivSample* s = ds.sample[i];
|
||||||
|
|
||||||
if (s->dataDPCM) {
|
if (s->samples>0) {
|
||||||
last_non_empty_sample = i;
|
last_non_empty_sample = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1835,21 +2004,42 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.sampleLen = ds.sample.size();
|
ds.sampleLen = ds.sample.size();
|
||||||
|
|
||||||
} else if (blockName == "SEQUENCES_VRC6") {
|
} else if (blockName == "SEQUENCES_VRC6") {
|
||||||
CHECK_BLOCK_VERSION(6);
|
CHECK_BLOCK_VERSION(6);
|
||||||
|
|
||||||
unsigned char* Indices = new unsigned char[128 * 5];
|
unsigned char* Indices = new unsigned char[128 * 5];
|
||||||
unsigned char* Types = new unsigned char[128 * 5];
|
unsigned char* Types = new unsigned char[128 * 5];
|
||||||
|
|
||||||
|
memset(Indices,0,128*5);
|
||||||
|
memset(Types,0,128*5);
|
||||||
|
|
||||||
unsigned int seq_count = reader.readI();
|
unsigned int seq_count = reader.readI();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < seq_count; i++) {
|
for (unsigned int i = 0; i < seq_count; i++) {
|
||||||
unsigned int index = reader.readI();
|
unsigned int index = reader.readI();
|
||||||
|
if (index>=128*5) {
|
||||||
|
logE("%d: index out of range",i);
|
||||||
|
lastError = "sequence index out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Indices[i] = index;
|
Indices[i] = index;
|
||||||
unsigned int type = reader.readI();
|
unsigned int type = reader.readI();
|
||||||
|
if (type>=128*5) {
|
||||||
|
logE("%d: type out of range",i);
|
||||||
|
lastError = "sequence type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Types[i] = type;
|
Types[i] = type;
|
||||||
|
|
||||||
|
if (index>=256 || type>=8) {
|
||||||
|
logE("%d: index/type out of range",i);
|
||||||
|
lastError = "sequence index/type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char size = reader.readC();
|
unsigned char size = reader.readC();
|
||||||
unsigned int setting = 0;
|
unsigned int setting = 0;
|
||||||
|
|
||||||
|
@ -1914,9 +2104,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
unsigned int release = reader.readI();
|
unsigned int release = reader.readI();
|
||||||
unsigned int setting = reader.readI();
|
unsigned int setting = reader.readI();
|
||||||
|
|
||||||
// macros[index][type].rel = release;
|
|
||||||
// macro_types[index][type] = setting;
|
|
||||||
|
|
||||||
for (int k = 0; k < (int)ds.ins.size(); k++) {
|
for (int k = 0; k < (int)ds.ins.size(); k++) {
|
||||||
DivInstrument* ins = ds.ins[k];
|
DivInstrument* ins = ds.ins[k];
|
||||||
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_VRC6 && hasSequence[k][Types[i]]) {
|
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_VRC6 && hasSequence[k][Types[i]]) {
|
||||||
|
@ -1937,19 +2124,40 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
delete[] Types;
|
delete[] Types;
|
||||||
} else if (blockName == "SEQUENCES_N163" || blockName == "SEQUENCES_N106") {
|
} else if (blockName == "SEQUENCES_N163" || blockName == "SEQUENCES_N106") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
// reader.seek(blockSize,SEEK_CUR);
|
|
||||||
|
|
||||||
unsigned char* Indices = new unsigned char[128 * 5];
|
unsigned char* Indices = new unsigned char[128 * 5];
|
||||||
unsigned char* Types = new unsigned char[128 * 5];
|
unsigned char* Types = new unsigned char[128 * 5];
|
||||||
|
|
||||||
|
memset(Indices,0,128*5);
|
||||||
|
memset(Types,0,128*5);
|
||||||
|
|
||||||
unsigned int seq_count = reader.readI();
|
unsigned int seq_count = reader.readI();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < seq_count; i++) {
|
for (unsigned int i = 0; i < seq_count; i++) {
|
||||||
unsigned int index = reader.readI();
|
unsigned int index = reader.readI();
|
||||||
|
if (index>=128*5) {
|
||||||
|
logE("%d: index out of range",i);
|
||||||
|
lastError = "sequence index out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Indices[i] = index;
|
Indices[i] = index;
|
||||||
unsigned int type = reader.readI();
|
unsigned int type = reader.readI();
|
||||||
|
if (type>=128*5) {
|
||||||
|
logE("%d: type out of range",i);
|
||||||
|
lastError = "sequence type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Types[i] = type;
|
Types[i] = type;
|
||||||
|
|
||||||
|
if (index>=256 || type>=8) {
|
||||||
|
logE("%d: index/type out of range",i);
|
||||||
|
lastError = "sequence index/type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char size = reader.readC();
|
unsigned char size = reader.readC();
|
||||||
unsigned int setting = 0;
|
unsigned int setting = 0;
|
||||||
|
|
||||||
|
@ -1974,7 +2182,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
DivInstrument* ins = ds.ins[k];
|
DivInstrument* ins = ds.ins[k];
|
||||||
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_N163 && hasSequence[k][Types[i]]) {
|
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_N163 && hasSequence[k][Types[i]]) {
|
||||||
copyMacro(ins, ¯os[index][type], type, setting);
|
copyMacro(ins, ¯os[index][type], type, setting);
|
||||||
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1984,19 +2191,40 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
|
|
||||||
} else if (blockName == "SEQUENCES_S5B") {
|
} else if (blockName == "SEQUENCES_S5B") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
// reader.seek(blockSize,SEEK_CUR);
|
|
||||||
|
|
||||||
unsigned char* Indices = new unsigned char[128 * 5];
|
unsigned char* Indices = new unsigned char[128 * 5];
|
||||||
unsigned char* Types = new unsigned char[128 * 5];
|
unsigned char* Types = new unsigned char[128 * 5];
|
||||||
|
|
||||||
|
memset(Indices,0,128*5);
|
||||||
|
memset(Types,0,128*5);
|
||||||
|
|
||||||
unsigned int seq_count = reader.readI();
|
unsigned int seq_count = reader.readI();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < seq_count; i++) {
|
for (unsigned int i = 0; i < seq_count; i++) {
|
||||||
unsigned int index = reader.readI();
|
unsigned int index = reader.readI();
|
||||||
|
if (index>=128*5) {
|
||||||
|
logE("%d: index out of range",i);
|
||||||
|
lastError = "sequence index out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Indices[i] = index;
|
Indices[i] = index;
|
||||||
unsigned int type = reader.readI();
|
unsigned int type = reader.readI();
|
||||||
|
if (type>=128*5) {
|
||||||
|
logE("%d: type out of range",i);
|
||||||
|
lastError = "sequence type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Types[i] = type;
|
Types[i] = type;
|
||||||
|
|
||||||
|
if (index>=256 || type>=8) {
|
||||||
|
logE("%d: index/type out of range",i);
|
||||||
|
lastError = "sequence index/type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char size = reader.readC();
|
unsigned char size = reader.readC();
|
||||||
unsigned int setting = 0;
|
unsigned int setting = 0;
|
||||||
|
|
||||||
|
@ -2021,7 +2249,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
DivInstrument* ins = ds.ins[k];
|
DivInstrument* ins = ds.ins[k];
|
||||||
if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_AY && hasSequence[k][type]) {
|
if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_AY && hasSequence[k][type]) {
|
||||||
copyMacro(ins, ¯os[index][type], type, setting);
|
copyMacro(ins, ¯os[index][type], type, setting);
|
||||||
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2034,14 +2261,36 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
unsigned char* Indices = new unsigned char[128 * 5];
|
unsigned char* Indices = new unsigned char[128 * 5];
|
||||||
unsigned char* Types = new unsigned char[128 * 5];
|
unsigned char* Types = new unsigned char[128 * 5];
|
||||||
|
|
||||||
|
memset(Indices,0,128*5);
|
||||||
|
memset(Types,0,128*5);
|
||||||
|
|
||||||
unsigned int seq_count = reader.readI();
|
unsigned int seq_count = reader.readI();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < seq_count; i++) {
|
for (unsigned int i = 0; i < seq_count; i++) {
|
||||||
unsigned int index = reader.readI();
|
unsigned int index = reader.readI();
|
||||||
|
if (index>=128*5) {
|
||||||
|
logE("%d: index out of range",i);
|
||||||
|
lastError = "sequence index out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Indices[i] = index;
|
Indices[i] = index;
|
||||||
unsigned int type = reader.readI();
|
unsigned int type = reader.readI();
|
||||||
|
if (type>=128*5) {
|
||||||
|
logE("%d: type out of range",i);
|
||||||
|
lastError = "sequence type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Types[i] = type;
|
Types[i] = type;
|
||||||
|
|
||||||
|
if (index>=256 || type>=8) {
|
||||||
|
logE("%d: index/type out of range",i);
|
||||||
|
lastError = "sequence index/type out of range";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char size = reader.readC();
|
unsigned char size = reader.readC();
|
||||||
unsigned int setting = 0;
|
unsigned int setting = 0;
|
||||||
|
|
||||||
|
@ -2066,7 +2315,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
DivInstrument* ins = ds.ins[k];
|
DivInstrument* ins = ds.ins[k];
|
||||||
if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_C64 && hasSequence[k][type]) {
|
if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_C64 && hasSequence[k][type]) {
|
||||||
copyMacro(ins, ¯os[index][type], type, setting);
|
copyMacro(ins, ¯os[index][type], type, setting);
|
||||||
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2075,31 +2323,33 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
delete[] Types;
|
delete[] Types;
|
||||||
} else if (blockName == "JSON") {
|
} else if (blockName == "JSON") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
|
logW("block JSON not supported...");
|
||||||
reader.seek(blockSize, SEEK_CUR);
|
reader.seek(blockSize, SEEK_CUR);
|
||||||
} else if (blockName == "PARAMS_EMU") {
|
} else if (blockName == "PARAMS_EMU") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
|
logW("block PARAMS_EMU not supported...");
|
||||||
reader.seek(blockSize, SEEK_CUR);
|
reader.seek(blockSize, SEEK_CUR);
|
||||||
} else if (blockName == "DETUNETABLES") {
|
} else if (blockName == "DETUNETABLES") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
|
logW("block DETUNETABLES not supported...");
|
||||||
reader.seek(blockSize, SEEK_CUR);
|
reader.seek(blockSize, SEEK_CUR);
|
||||||
} else if (blockName == "COMMENTS") {
|
} else if (blockName == "COMMENTS") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
// reader.seek(blockSize,SEEK_CUR);
|
|
||||||
unsigned int display_comment = reader.readI();
|
unsigned int display_comment = reader.readI();
|
||||||
(void)display_comment;
|
|
||||||
|
|
||||||
char ch = 1;
|
logV("displayComment: %d",display_comment);
|
||||||
|
|
||||||
do {
|
char ch = 0;
|
||||||
|
|
||||||
|
// why not readString?
|
||||||
|
while (true) {
|
||||||
ch = reader.readC();
|
ch = reader.readC();
|
||||||
String sss = String() + ch;
|
if (ch==0) break;
|
||||||
ds.subsong[0]->notes += sss;
|
ds.subsong[0]->notes += ch;
|
||||||
} while (ch != 0);
|
}
|
||||||
|
|
||||||
// ds.subsong[0]->notes = reader.readS();
|
|
||||||
} else if (blockName == "PARAMS_EXTRA") {
|
} else if (blockName == "PARAMS_EXTRA") {
|
||||||
CHECK_BLOCK_VERSION(3);
|
CHECK_BLOCK_VERSION(3);
|
||||||
// reader.seek(blockSize,SEEK_CUR);
|
|
||||||
unsigned int linear_pitch = reader.readI();
|
unsigned int linear_pitch = reader.readI();
|
||||||
|
|
||||||
ds.linearPitch = linear_pitch == 0 ? 0 : 2;
|
ds.linearPitch = linear_pitch == 0 ? 0 : 2;
|
||||||
|
@ -2112,11 +2362,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
if (blockVersion >= 3) {
|
if (blockVersion >= 3) {
|
||||||
unsigned char flats = reader.readC();
|
unsigned char flats = reader.readC();
|
||||||
(void)flats;
|
logV("flats: %d",(int)flats);
|
||||||
}
|
}
|
||||||
} else if (blockName == "TUNING") {
|
} else if (blockName == "TUNING") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
// reader.seek(blockSize,SEEK_CUR);
|
|
||||||
if (blockVersion == 1) {
|
if (blockVersion == 1) {
|
||||||
int fineTuneCents = reader.readC() * 100;
|
int fineTuneCents = reader.readC() * 100;
|
||||||
fineTuneCents += reader.readC();
|
fineTuneCents += reader.readC();
|
||||||
|
@ -2125,6 +2374,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
} else if (blockName == "BOOKMARKS") {
|
} else if (blockName == "BOOKMARKS") {
|
||||||
CHECK_BLOCK_VERSION(1);
|
CHECK_BLOCK_VERSION(1);
|
||||||
|
logW("block BOOKMARKS not supported...");
|
||||||
reader.seek(blockSize, SEEK_CUR);
|
reader.seek(blockSize, SEEK_CUR);
|
||||||
} else {
|
} else {
|
||||||
logE("block %s is unknown!", blockName);
|
logE("block %s is unknown!", blockName);
|
||||||
|
@ -2152,6 +2402,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|
||||||
|
if (index>=(int)ds.ins.size()) continue;
|
||||||
|
|
||||||
DivInstrument* ins = ds.ins[index];
|
DivInstrument* ins = ds.ins[index];
|
||||||
|
|
||||||
if (ins->type == DIV_INS_FM) {
|
if (ins->type == DIV_INS_FM) {
|
||||||
|
|
|
@ -1897,7 +1897,7 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path, bool
|
||||||
bool isOldFurnaceIns=false;
|
bool isOldFurnaceIns=false;
|
||||||
try {
|
try {
|
||||||
reader.read(magic,4);
|
reader.read(magic,4);
|
||||||
if (memcmp("FINS",magic,4)==0) {
|
if (memcmp("FINS",magic,4)==0 || memcmp("FINB",magic,4)==0) {
|
||||||
isFurnaceInstr=true;
|
isFurnaceInstr=true;
|
||||||
logV("found a new Furnace ins");
|
logV("found a new Furnace ins");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3005,6 +3005,8 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version, DivS
|
||||||
type=1;
|
type=1;
|
||||||
} else if (memcmp(magic,"FINS",4)==0) {
|
} else if (memcmp(magic,"FINS",4)==0) {
|
||||||
type=2;
|
type=2;
|
||||||
|
} else if (memcmp(magic,"FINB",4)==0) { // DIV_FUR_VARIANT_B
|
||||||
|
type=2;
|
||||||
} else {
|
} else {
|
||||||
logE("invalid instrument header!");
|
logE("invalid instrument header!");
|
||||||
return DIV_DATA_INVALID_HEADER;
|
return DIV_DATA_INVALID_HEADER;
|
||||||
|
|
|
@ -1303,7 +1303,7 @@ void DivEngine::registerSystems() {
|
||||||
|
|
||||||
sysDefs[DIV_SYSTEM_VRC7]=new DivSysDef(
|
sysDefs[DIV_SYSTEM_VRC7]=new DivSysDef(
|
||||||
"Konami VRC7", NULL, 0x9d, 0, 6, true, false, 0x151, false, 0, 0, 0,
|
"Konami VRC7", NULL, 0x9d, 0, 6, true, false, 0x151, false, 0, 0, 0,
|
||||||
"like OPLL, but even more cost reductions applied. three less FM channels, and no drums mode...",
|
"like OPLL, but even more cost reductions applied. three FM channels went missing, and drums mode did as well...",
|
||||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"},
|
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"},
|
||||||
{"F1", "F2", "F3", "F4", "F5", "F6"},
|
{"F1", "F2", "F3", "F4", "F5", "F6"},
|
||||||
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
|
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
|
||||||
|
|
|
@ -71,6 +71,7 @@ const char* aboutLine[]={
|
||||||
"Aburtos",
|
"Aburtos",
|
||||||
"ActualNK358",
|
"ActualNK358",
|
||||||
"akumanatt",
|
"akumanatt",
|
||||||
|
"aloelucidity",
|
||||||
"AmigaX",
|
"AmigaX",
|
||||||
"AquaDoesStuff",
|
"AquaDoesStuff",
|
||||||
"AURORA*FIELDS",
|
"AURORA*FIELDS",
|
||||||
|
|
|
@ -4391,7 +4391,7 @@ bool FurnaceGUI::loop() {
|
||||||
toggleMobileUI(!mobileUI);
|
toggleMobileUI(!mobileUI);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ImGui::MenuItem("manage presets...",BIND_FOR(GUI_ACTION_WINDOW_USER_PRESETS))) {
|
if (ImGui::MenuItem("user systems...",BIND_FOR(GUI_ACTION_WINDOW_USER_PRESETS))) {
|
||||||
userPresetsOpen=true;
|
userPresetsOpen=true;
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("settings...",BIND_FOR(GUI_ACTION_WINDOW_SETTINGS))) {
|
if (ImGui::MenuItem("settings...",BIND_FOR(GUI_ACTION_WINDOW_SETTINGS))) {
|
||||||
|
|
|
@ -1265,6 +1265,7 @@ struct FurnaceGUISysDef {
|
||||||
String definition;
|
String definition;
|
||||||
std::vector<FurnaceGUISysDefChip> orig;
|
std::vector<FurnaceGUISysDefChip> orig;
|
||||||
std::vector<FurnaceGUISysDef> subDefs;
|
std::vector<FurnaceGUISysDef> subDefs;
|
||||||
|
void bake();
|
||||||
FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def, const char* e=NULL);
|
FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def, const char* e=NULL);
|
||||||
FurnaceGUISysDef(const char* n, const char* def, DivEngine* e);
|
FurnaceGUISysDef(const char* n, const char* def, DivEngine* e);
|
||||||
};
|
};
|
||||||
|
@ -2089,7 +2090,7 @@ class FurnaceGUI {
|
||||||
displayRenderTime(0),
|
displayRenderTime(0),
|
||||||
maxUndoSteps(100),
|
maxUndoSteps(100),
|
||||||
vibrationStrength(0.5f),
|
vibrationStrength(0.5f),
|
||||||
vibrationLength(100),
|
vibrationLength(20),
|
||||||
mainFontPath(""),
|
mainFontPath(""),
|
||||||
headFontPath(""),
|
headFontPath(""),
|
||||||
patFontPath(""),
|
patFontPath(""),
|
||||||
|
|
|
@ -7469,7 +7469,7 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
memset(oldData,0,256*sizeof(int));
|
memset(oldData,0,256*sizeof(int));
|
||||||
memcpy(oldData,lastMacroDesc.macro->val,lastMacroDesc.macro->len*sizeof(int));
|
memcpy(oldData,lastMacroDesc.macro->val,lastMacroDesc.macro->len*sizeof(int));
|
||||||
|
|
||||||
lastMacroDesc.macro->len=MIN(128,((double)lastMacroDesc.macro->len*(macroScaleX/100.0)));
|
lastMacroDesc.macro->len=MIN(255,((double)lastMacroDesc.macro->len*(macroScaleX/100.0)));
|
||||||
|
|
||||||
for (int i=0; i<lastMacroDesc.macro->len; i++) {
|
for (int i=0; i<lastMacroDesc.macro->len; i++) {
|
||||||
int val=0;
|
int val=0;
|
||||||
|
|
|
@ -3102,11 +3102,9 @@ void FurnaceGUI::initSystemPresets() {
|
||||||
CATEGORY_END;
|
CATEGORY_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def, const char* e):
|
void FurnaceGUISysDef::bake() {
|
||||||
name(n),
|
|
||||||
extra((e==NULL)?"":e) {
|
|
||||||
orig=def;
|
|
||||||
int index=0;
|
int index=0;
|
||||||
|
definition="";
|
||||||
for (FurnaceGUISysDefChip& i: orig) {
|
for (FurnaceGUISysDefChip& i: orig) {
|
||||||
definition+=fmt::sprintf(
|
definition+=fmt::sprintf(
|
||||||
"id%d=%d\nvol%d=%f\npan%d=%f\nflags%d=%s\n",
|
"id%d=%d\nvol%d=%f\npan%d=%f\nflags%d=%s\n",
|
||||||
|
@ -3126,12 +3124,19 @@ FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceG
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def, const char* e):
|
||||||
|
name(n),
|
||||||
|
extra((e==NULL)?"":e) {
|
||||||
|
orig=def;
|
||||||
|
bake();
|
||||||
|
}
|
||||||
|
|
||||||
FurnaceGUISysDef::FurnaceGUISysDef(const char* n, const char* def, DivEngine* e):
|
FurnaceGUISysDef::FurnaceGUISysDef(const char* n, const char* def, DivEngine* e):
|
||||||
name(n),
|
name(n),
|
||||||
definition(def) {
|
definition(taDecodeBase64(def)) {
|
||||||
// extract definition
|
// extract definition
|
||||||
DivConfig conf;
|
DivConfig conf;
|
||||||
conf.loadFromBase64(def);
|
conf.loadFromMemory(definition.c_str());
|
||||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
String nextStr=fmt::sprintf("id%d",i);
|
String nextStr=fmt::sprintf("id%d",i);
|
||||||
int id=conf.getInt(nextStr.c_str(),0);
|
int id=conf.getInt(nextStr.c_str(),0);
|
||||||
|
|
|
@ -4110,7 +4110,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
settings.centerPopup=conf.getInt("centerPopup",1);
|
settings.centerPopup=conf.getInt("centerPopup",1);
|
||||||
|
|
||||||
settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f);
|
settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f);
|
||||||
settings.vibrationLength=conf.getInt("vibrationLength",100);
|
settings.vibrationLength=conf.getInt("vibrationLength",20);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groups&GUI_SETTINGS_AUDIO) {
|
if (groups&GUI_SETTINGS_AUDIO) {
|
||||||
|
|
|
@ -453,13 +453,17 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
||||||
if (sampRate>65536) sampRate=65536;
|
if (sampRate>65536) sampRate=65536;
|
||||||
altered=true;
|
altered=true;
|
||||||
} rightClickable
|
} rightClickable
|
||||||
DivPlatformGBAMinMod* dispatch=(DivPlatformGBAMinMod*)e->getDispatch(chan);
|
if (chan>=0) {
|
||||||
float maxCPU=dispatch->maxCPU*100;
|
DivPlatformGBAMinMod* dispatch=(DivPlatformGBAMinMod*)e->getDispatch(chan);
|
||||||
ImGui::Text("Actual sample rate: %d Hz", dispatch->chipClock);
|
if (dispatch!=NULL) {
|
||||||
if (maxCPU>90) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]);
|
float maxCPU=dispatch->maxCPU*100;
|
||||||
ImGui::Text("Max mixer CPU usage: %.0f%%", maxCPU);
|
ImGui::Text("Actual sample rate: %d Hz", dispatch->chipClock);
|
||||||
if (maxCPU>90) ImGui::PopStyleColor();
|
if (maxCPU>90) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]);
|
||||||
FurnaceGUI::popWarningColor();
|
ImGui::Text("Max mixer CPU usage: %.0f%%",maxCPU);
|
||||||
|
if (maxCPU>90) ImGui::PopStyleColor();
|
||||||
|
FurnaceGUI::popWarningColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (altered) {
|
if (altered) {
|
||||||
e->lockSave([&]() {
|
e->lockSave([&]() {
|
||||||
flags.set("volScale",volScale);
|
flags.set("volScale",volScale);
|
||||||
|
|
|
@ -453,7 +453,7 @@ struct FurnaceCV {
|
||||||
newHiScore(false),
|
newHiScore(false),
|
||||||
playSongs(true),
|
playSongs(true),
|
||||||
pleaseInitSongs(false),
|
pleaseInitSongs(false),
|
||||||
lives(3),
|
lives(5),
|
||||||
respawnTime(0),
|
respawnTime(0),
|
||||||
stage(0),
|
stage(0),
|
||||||
shotType(0),
|
shotType(0),
|
||||||
|
|
|
@ -177,7 +177,7 @@ bool FurnaceGUI::loadUserPresets(bool redundancy) {
|
||||||
}
|
}
|
||||||
indent>>=1;
|
indent>>=1;
|
||||||
|
|
||||||
if (!key.empty() && !value.empty()) {
|
if (!key.empty()) {
|
||||||
std::vector<FurnaceGUISysDef>* where=digDeep(userCategory->systems,indent);
|
std::vector<FurnaceGUISysDef>* where=digDeep(userCategory->systems,indent);
|
||||||
where->push_back(FurnaceGUISysDef(key.c_str(),value.c_str(),e));
|
where->push_back(FurnaceGUISysDef(key.c_str(),value.c_str(),e));
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ void writeSubEntries(FILE* f, std::vector<FurnaceGUISysDef>& entries, int depth)
|
||||||
for (int i=0; i<depth; i++) {
|
for (int i=0; i<depth; i++) {
|
||||||
data+=" ";
|
data+=" ";
|
||||||
}
|
}
|
||||||
data+=fmt::sprintf("%s=%s\n",safeName,i.definition);
|
data+=fmt::sprintf("%s=%s\n",safeName,taEncodeBase64(i.definition));
|
||||||
fputs(data.c_str(),f);
|
fputs(data.c_str(),f);
|
||||||
|
|
||||||
writeSubEntries(f,i.subDefs,depth+1);
|
writeSubEntries(f,i.subDefs,depth+1);
|
||||||
|
@ -322,7 +322,7 @@ void FurnaceGUI::drawUserPresets() {
|
||||||
nextWindow=GUI_WINDOW_NOTHING;
|
nextWindow=GUI_WINDOW_NOTHING;
|
||||||
}
|
}
|
||||||
if (!userPresetsOpen) return;
|
if (!userPresetsOpen) return;
|
||||||
if (ImGui::Begin("User Presets",&userPresetsOpen,globalWinFlags)) {
|
if (ImGui::Begin("User Systems",&userPresetsOpen,globalWinFlags)) {
|
||||||
FurnaceGUISysCategory* userCategory=NULL;
|
FurnaceGUISysCategory* userCategory=NULL;
|
||||||
for (FurnaceGUISysCategory& i: sysCategories) {
|
for (FurnaceGUISysCategory& i: sysCategories) {
|
||||||
if (strcmp(i.name,"User")==0) {
|
if (strcmp(i.name,"User")==0) {
|
||||||
|
@ -335,34 +335,173 @@ void FurnaceGUI::drawUserPresets() {
|
||||||
|
|
||||||
if (userCategory==NULL) {
|
if (userCategory==NULL) {
|
||||||
ImGui::Text("Error! User category does not exist!");
|
ImGui::Text("Error! User category does not exist!");
|
||||||
} else if (ImGui::BeginTable("UserPresets",2,ImGuiTableFlags_BordersInnerV)) {
|
} else if (ImGui::BeginTable("UserPresets",2,ImGuiTableFlags_BordersInnerV,ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()))) {
|
||||||
|
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.25f);
|
||||||
|
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.75f);
|
||||||
// preset list
|
// preset list
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (ImGui::Button(ICON_FA_PLUS "##AddPreset")) {
|
if (ImGui::BeginChild("UList",ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()))) {
|
||||||
userCategory->systems.push_back(FurnaceGUISysDef("New Preset",{}));
|
ImGui::AlignTextToFramePadding();
|
||||||
selectedUserPreset.clear();
|
ImGui::Text("Systems");
|
||||||
selectedUserPreset.push_back(userCategory->systems.size()-1);
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_FA_PLUS "##AddPreset")) {
|
||||||
|
userCategory->systems.push_back(FurnaceGUISysDef("New Preset",{}));
|
||||||
|
selectedUserPreset.clear();
|
||||||
|
selectedUserPreset.push_back(userCategory->systems.size()-1);
|
||||||
|
}
|
||||||
|
printPresets(userCategory->systems,0,depthStack);
|
||||||
}
|
}
|
||||||
printPresets(userCategory->systems,0,depthStack);
|
ImGui::EndChild();
|
||||||
|
|
||||||
// editor
|
// editor
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (selectedUserPreset.empty()) {
|
if (ImGui::BeginChild("UEdit",ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()))) {
|
||||||
ImGui::Text("select a preset");
|
if (selectedUserPreset.empty()) {
|
||||||
} else {
|
ImGui::Text("select a preset");
|
||||||
FurnaceGUISysDef* preset=selectPreset(userCategory->systems);
|
} else {
|
||||||
|
FurnaceGUISysDef* preset=selectPreset(userCategory->systems);
|
||||||
|
bool doRemovePreset=false;
|
||||||
|
|
||||||
if (preset!=NULL) {
|
if (preset!=NULL) {
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text("Name");
|
ImGui::Text("Name");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Remove").x-ImGui::GetStyle().ItemSpacing.x*2.0-ImGui::GetStyle().ItemInnerSpacing.x*2.0);
|
||||||
ImGui::InputText("##PName",&preset->name);
|
ImGui::InputText("##PName",&preset->name);
|
||||||
ImGui::Separator();
|
ImGui::SameLine();
|
||||||
ImGui::Text("the rest...");
|
pushDestColor();
|
||||||
|
if (ImGui::Button("Remove##UPresetRemove")) {
|
||||||
|
doRemovePreset=true;
|
||||||
|
}
|
||||||
|
popDestColor();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
int doRemove=-1;
|
||||||
|
bool mustBake=false;
|
||||||
|
|
||||||
|
for (size_t i=0; i<preset->orig.size(); i++) {
|
||||||
|
String tempID;
|
||||||
|
FurnaceGUISysDefChip& chip=preset->orig[i];
|
||||||
|
|
||||||
|
bool doInvert=(chip.vol<0);
|
||||||
|
float vol=fabs(chip.vol);
|
||||||
|
ImGui::PushID(i);
|
||||||
|
|
||||||
|
tempID=fmt::sprintf("%s##USystem",getSystemName(chip.sys));
|
||||||
|
ImGui::Button(tempID.c_str(),ImVec2(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Invert").x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0,0));
|
||||||
|
if (ImGui::BeginPopupContextItem("SysPickerCU",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||||
|
DivSystem picked=systemPicker();
|
||||||
|
if (picked!=DIV_SYSTEM_NULL) {
|
||||||
|
chip.sys=picked;
|
||||||
|
mustBake=true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Checkbox("Invert",&doInvert)) {
|
||||||
|
chip.vol=-chip.vol;
|
||||||
|
mustBake=true;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
pushDestColor();
|
||||||
|
if (ImGui::Button(ICON_FA_MINUS "##USysRemove")) {
|
||||||
|
doRemove=i;
|
||||||
|
mustBake=true;
|
||||||
|
}
|
||||||
|
popDestColor();
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0);
|
||||||
|
if (CWSliderFloat("Volume",&vol,0.0f,3.0f)) {
|
||||||
|
if (doInvert) {
|
||||||
|
if (vol<0.0001) vol=0.0001;
|
||||||
|
}
|
||||||
|
if (vol<0) vol=0;
|
||||||
|
if (vol>10) vol=10;
|
||||||
|
chip.vol=doInvert?-vol:vol;
|
||||||
|
mustBake=true;
|
||||||
|
} rightClickable
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0);
|
||||||
|
if (CWSliderFloat("Panning",&chip.pan,-1.0f,1.0f)) {
|
||||||
|
if (chip.pan<-1.0f) chip.pan=-1.0f;
|
||||||
|
if (chip.pan>1.0f) chip.pan=1.0f;
|
||||||
|
mustBake=true;
|
||||||
|
} rightClickable
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0);
|
||||||
|
if (CWSliderFloat("Front/Rear",&chip.panFR,-1.0f,1.0f)) {
|
||||||
|
if (chip.panFR<-1.0f) chip.panFR=-1.0f;
|
||||||
|
if (chip.panFR>1.0f) chip.panFR=1.0f;
|
||||||
|
mustBake=true;
|
||||||
|
} rightClickable
|
||||||
|
|
||||||
|
if (ImGui::TreeNode("Configure")) {
|
||||||
|
DivConfig sysFlags;
|
||||||
|
sysFlags.loadFromBase64(chip.flags.c_str());
|
||||||
|
if (drawSysConf(-1,i,chip.sys,sysFlags,false)) {
|
||||||
|
chip.flags=sysFlags.toBase64();
|
||||||
|
mustBake=true;
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doRemove>=0) {
|
||||||
|
preset->orig.erase(preset->orig.begin()+doRemove);
|
||||||
|
mustBake=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Button(ICON_FA_PLUS "##SysAddU");
|
||||||
|
if (ImGui::BeginPopupContextItem("SysPickerU",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||||
|
DivSystem picked=systemPicker();
|
||||||
|
if (picked!=DIV_SYSTEM_NULL) {
|
||||||
|
preset->orig.push_back(FurnaceGUISysDefChip(picked,1.0f,0.0f,""));
|
||||||
|
mustBake=true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::Text("Advanced");
|
||||||
|
if (ImGui::InputTextMultiline("##UExtra",&preset->extra,ImVec2(ImGui::GetContentRegionAvail().x,120.0f*dpiScale),ImGuiInputTextFlags_UndoRedo)) {
|
||||||
|
mustBake=true;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip(
|
||||||
|
"insert additional settings in `option=value` format.\n"
|
||||||
|
"available options:\n"
|
||||||
|
"- tickRate"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mustBake) preset->bake();
|
||||||
|
} else {
|
||||||
|
selectedUserPreset.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doRemovePreset) {
|
||||||
|
std::vector<FurnaceGUISysDef>& items=userCategory->systems;
|
||||||
|
FurnaceGUISysDef* target=NULL;
|
||||||
|
for (size_t i=0; i<selectedUserPreset.size(); i++) {
|
||||||
|
if (selectedUserPreset[i]<0 || selectedUserPreset[i]>(int)items.size()) break;
|
||||||
|
target=&items[selectedUserPreset[i]];
|
||||||
|
if (i<selectedUserPreset.size()-1) {
|
||||||
|
items=target->subDefs;
|
||||||
|
} else {
|
||||||
|
items.erase(items.begin()+selectedUserPreset[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedUserPreset.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue