Merge branch 'master' into ZSMv1

This commit is contained in:
ZeroByteOrg 2022-06-29 13:14:15 -05:00
commit 40d67d7bb5
757 changed files with 4340 additions and 359 deletions

View File

@ -95,6 +95,7 @@ else()
if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(WITH_OUR_MALLOC ON CACHE BOOL "aaa" FORCE)
endif()
set(BUILD_TESTS OFF CACHE BOOL "come on" FORCE)
add_subdirectory(extern/fftw EXCLUDE_FROM_ALL)
list(APPEND DEPENDENCIES_INCLUDE_DIRS extern/fftw/api)
list(APPEND DEPENDENCIES_LIBRARIES fftw3)
@ -252,7 +253,7 @@ src/audio/midi.cpp
)
if (USE_SDL2)
list(APPEND AUDIO_SOURCES src/audio/sdl.cpp)
list(APPEND AUDIO_SOURCES src/audio/sdlAudio.cpp)
endif()
if (WITH_JACK)
@ -452,6 +453,10 @@ src/engine/platform/rf5c68.cpp
src/engine/platform/dummy.cpp
)
if (USE_SNDFILE)
list(APPEND ENGINE_SOURCES src/engine/sfWrapper.cpp)
endif()
if (WIN32)
list(APPEND ENGINE_SOURCES src/utfutils.cpp)
list(APPEND ENGINE_SOURCES src/engine/winStuff.cpp)
@ -539,6 +544,24 @@ endif()
if (NOT WIN32 AND NOT APPLE)
list(APPEND GUI_SOURCES src/gui/icon.c)
include(CheckIncludeFile)
CHECK_INCLUDE_FILE(sys/io.h SYS_IO_FOUND)
CHECK_INCLUDE_FILE(linux/input.h LINUX_INPUT_FOUND)
CHECK_INCLUDE_FILE(linux/kd.h LINUX_KD_FOUND)
if (SYS_IO_FOUND)
list(APPEND DEPENDENCIES_DEFINES HAVE_SYS_IO)
message(STATUS "PC speaker output: outb()")
endif()
if (LINUX_INPUT_FOUND)
list(APPEND DEPENDENCIES_DEFINES HAVE_LINUX_INPUT)
message(STATUS "PC speaker output: evdev")
endif()
if (LINUX_KD_FOUND)
list(APPEND DEPENDENCIES_DEFINES HAVE_LINUX_KD)
message(STATUS "PC speaker output: KIOCSOUND")
endif()
endif()
set(USED_SOURCES ${ENGINE_SOURCES} ${AUDIO_SOURCES} src/main.cpp)

190
README.md
View File

@ -1,8 +1,8 @@
# Furnace Tracker
![screenshot](papers/screenshot1.png)
![screenshot](papers/screenshot2.png)
this is a multi-system chiptune tracker.
the biggest multi-system chiptune tracker ever made!
[downloads](#downloads) | [discussion/help](#quick-references) | [developer info](#developer-info) | [unofficial packages](#unofficial-packages) | [FAQ](#frequently-asked-questions)
@ -15,34 +15,91 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
## features
- supports the following systems:
- Sega Genesis
- Sega Master System
- over 50 sound chips - and counting:
- Yamaha FM chips:
- YM2151 (OPM)
- YM2203 (OPN)
- YM2413 (OPLL)
- YM2414 (OPZ) used in Yamaha TX81Z
- YM2608 (OPNA) used in PC-98
- YM2610 (OPNB) used in Neo Geo
- YM2610B (OPNB2)
- YM2612 (OPN2) used in Sega Genesis and FM Towns
- YM3526 (OPL) used in C64 Sound Expander
- YM3812 (OPL2)
- YMF262 (OPL3) with full 4-op support!
- Y8950 (OPL with ADPCM)
- square wave chips:
- AY-3-8910/YM2149(F) used in several computers and game consoles
- Commodore VIC used in the VIC-20
- Microchip AY8930
- TI SN76489 used in Sega Master System and BBC Micro
- PC Speaker
- Philips SAA1099 used in SAM Coupé
- sample chips:
- Amiga
- SegaPCM - all 16 channels
- Capcom QSound
- Yamaha YMZ280B (PCMD8)
- Ricoh RF5C68 used in Sega CD and FM Towns
- OKI MSM6258 and MSM6295
- wavetable chips:
- HuC6280 used in PC Engine
- Konami Bubble System WSG
- Konami SCC/SCC+
- Namco arcade chips (WSG/C15/C30)
- WonderSwan
- Seta/Allumer X1-010
- NES (Ricoh 2A03/2A07), with additional expansion sound support:
- Konami VRC6
- Konami VRC7
- MMC5
- Famicom Disk System
- Sunsoft 5B
- Namco 163
- Family Noraebang (OPLL)
- SID (6581/8580) used in Commodore 64
- Mikey used in Atari Lynx
- ZX Spectrum beeper (SFX-like engine)
- TIA used in Atari 2600
- Game Boy
- PC Engine
- NES
- Commodore 64
- Yamaha YM2151 (plus PCM)
- Neo Geo
- AY-3-8910 (ZX Spectrum, Atari ST, etc.)
- Microchip AY8930
- Philips SAA1099
- Amiga
- TIA (Atari 2600/7800)
- multiple sound chips in a single song!
- DefleMask compatibility - loads .dmf modules, .dmp instruments and .dmw wavetables
- clean-room design (guesswork and ABX tests only, no decompilation involved)
- bug/quirk implementation for increased playback accuracy
- VGM and audio file export
- accurate emulation cores whether possible (Nuked, MAME, SameBoy, Mednafen PCE, puNES, reSID, Stella, SAASound and ymfm)
- additional features on top:
- modern/fantasy:
- Commander X16 VERA
- tildearrow Sound Unit
- mix and match sound chips!
- over 200 ready to use presets from computers, game consoles and arcade boards...
- ...or create your own - up to 32 of them or a total of 128 channels!
- DefleMask compatibility
- loads .dmf modules from all versions (beta 1 to 1.1.3)
- saves .dmf modules - both modern and legacy
- Furnace doubles as a module downgrader
- loads .dmp instruments and .dmw wavetables as well
- clean-room design (guesswork and ABX tests only, no decompilation involved)
- bug/quirk implementation for increased playback accuracy through compatibility flags
- VGM export
- modular layout that you may adapt to your needs
- audio file export - entire song, per system or per channel
- quality emulation cores (Nuked, MAME, SameBoy, Mednafen PCE, NSFplay, puNES, reSID, Stella, SAASound, vgsound_emu and ymfm)
- wavetable synthesizer
- available on wavetable chips
- create complex sounds with ease - provide up to two wavetables, select and effect and let go!
- MIDI input support
- [Fractal Sound](https://gitlab.com/Natsumi/Fractal-Sound) support!
- the game-ready Sega Genesis/Mega Drive sound driver!
- compose your songs in Furnace using the Fractal Sound presets and then use them in your games with Fractal!
- additional features:
- FM macros!
- negative octaves
- arbitrary pitch samples
- sample loop points
- SSG envelopes in Neo Geo
- SSG envelopes and ADPCM-B in Neo Geo
- full duty/cutoff range in C64
- ability to change tempo mid-song with `Cxxx` effect (`xxx` between `000` and `3ff`)
- ability to change tempo mid-song
- multiple sub-songs in a module
- per-channel oscilloscope with waveform centering
- built-in sample editor
- chip mixing settings
- built-in visualizer in pattern view
- open-source under GPLv2 or later.
***
@ -59,7 +116,7 @@ some people have provided packages for Unix/Unix-like distributions. here's a li
- **Arch Linux**: [furnace-git is in the AUR.](https://aur.archlinux.org/packages/furnace-git) thank you Essem!
- **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt.
- **Nix**: [package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608.
- **OpenSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari.
- **openSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari.
***
# developer info
@ -73,11 +130,17 @@ if you can't download these artifacts (because GitHub requires you to be logged
## dependencies
- CMake
- SDL2
- zlib
- JACK (optional)
- JACK (optional, macOS/Linux only)
SDL2 and zlib are included as submodules.
if building under Windows or macOS, no additional dependencies are required.
otherwise, you may also need the following:
- libpulse
- libx11
- libasound
- libGL
some Linux distributions (e.g. Ubuntu or openSUSE) will require you to install the `-dev` versions of these.
## getting the source
@ -127,14 +190,20 @@ Available options:
| Name | Default | Description |
| :--: | :-----: | ----------- |
| `BUILD_GUI` | `ON` | Build the tracker (disable to build only a headless player) |
| `USE_RTMIDI` | `ON` | Build with MIDI support using RtMidi |
| `USE_SDL2` | `ON` | Build with SDL2 (required to build with GUI) |
| `USE_SNDFILE` | `ON` | Build with libsndfile (required in order to work with audio files) |
| `USE_BACKWARD` | `ON` | Use backward-cpp to print a backtrace on crash/abort |
| `WITH_JACK` | `ON` if system-installed JACK detected, otherwise `OFF` | Whether to build with JACK support. Auto-detects if JACK is available |
| `SYSTEM_FFTW` | `OFF` | Use a system-installed version of FFTW instead of the vendored one |
| `SYSTEM_FMT` | `OFF` | Use a system-installed version of fmt instead of the vendored one |
| `SYSTEM_LIBSNDFILE` | `OFF` | Use a system-installed version of libsndfile instead of the vendored one |
| `SYSTEM_RTMIDI` | `OFF` | Use a system-installed version of RtMidi instead of the vendored one |
| `SYSTEM_ZLIB` | `OFF` | Use a system-installed version of zlib instead of the vendored one |
| `SYSTEM_SDL2` | `OFF` | Use a system-installed version of SDL2 instead of the vendored one |
| `WARNINGS_ARE_ERRORS` | `OFF` (but consider enabling this & reporting any errors that arise from it!) | Whether warnings in furnace's C++ code should be treated as errors |
## usage
## console usage
```
./furnace
@ -156,36 +225,21 @@ this will play a compatible file and enable the commands view.
**note that these commands only actually work in Linux environments. on other command lines, such as Windows' Command Prompt, or MacOS Terminal, it may not work correctly.**
***
# notes
> how do I use Neo Geo SSG envelopes?
the following effects are provided:
- `22xy`: set envelope mode.
- `x` sets the envelope shape, which may be one of the following:
- `0: \___` decay
- `4: /___` attack once
- `8: \\\\` saw
- `9: \___` decay
- `A: \/\/` inverse obelisco
- `B: \¯¯¯` decay once
- `C: ////` inverse saw
- `D: /¯¯¯` attack
- `E: /\/\` obelisco
- `F: /___` attack once
- if `y` is 1 then the envelope will affect this channel.
- `23xx`: set envelope period low byte.
- `24xx`: set envelope period high byte.
- `25xx`: slide envelope period up.
- `26xx`: slide envelope period down.
a lower envelope period will make the envelope run faster.
***
# frequently asked questions
> woah! 50 sound chips?! I can't believe it!
yup, it's real.
> where's the manual?
see [papers/](papers/README.md). it's kind of incomplete, but at least the systems (sound chips) section is there.
> it doesn't open under macOS!
this is due to Apple's application signing policy. a workaround is to right click on the Furnace app icon and select Open.
> how do I use C64 absolute filter/duty?
on Instrument Editor in the C64 tab there are two options to toggle these.
@ -195,25 +249,27 @@ also provided are two effects:
- `4xxx`: set fine cutoff. `xxx` range is 000-7ff.
additionally, you can change the cutoff and/or duty as a macro inside an instrument by clicking the `absolute cutoff macro` and/or `absolute duty macro` checkbox at the bottom of the instrument. (for the filter, you also need to click the checkbox that says `volume macro is cutoff macro`.)
> Q: how do I use PCM on a PCM-capable system?
> how do I use PCM on a PCM-capable system?
A: Two possibilities: the recommended way is via creating the "Amiga/Sample" type instrument and assigning sample to it, or via old, Deflemask-compatible method, using `17xx` effect
two possibilities:
- the recommended way is by creating the "Sample" type instrument and assigning a sample to it.
- otherwise you may employ the DefleMask-compatible method, using `17xx` effect.
> Q: my song sounds very odd at a certain point
> my .dmf song sounds very odd at a certain point
A: file a bug report. use the Issues page. it's probably another playback inaccuracy.
file a bug report. use the Issues page. it's probably another playback inaccuracy.
> Q: my song sounds correct, but it doesn't in DefleMask
> my .dmf song sounds correct, but it doesn't in DefleMask
A: file a bug report **here**. it still is a playback inaccuracy.
file a bug report **here**. it still is a playback inaccuracy.
> Q: my C64 song sounds terrible after saving as .dmf!
> my song sounds terrible after saving as .dmf!
A: that's a limitation of the DefleMask format. save in Furnace song format instead (.fur).
the DefleMask format has several limitations. save in Furnace song format instead (.fur).
> Q: how do I solo channels?
> how do I solo channels?
A: right click on the channel name.
right click on the channel name.
***
# footnotes

View File

@ -1,8 +1,4 @@
# to-do for 0.6pre1
- implement Defle slide bug when using E1xy/E2xy and repeating origin note (requires format change)
# to-do for 0.6pre2 (as this requires new data structures)
# to-do for 0.6pre1.5-0.6pre2
- rewrite the system name detection function anyway
- this involves the addition of a new "system" field in the song (which solves the problem)

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/atmosphere.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/the_king_of_crisp.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/wolf3d.fur Normal file

Binary file not shown.

View File

@ -1019,6 +1019,10 @@ static void OPLL_Operator(opll_t *chip) {
}
chip->ch_out = ismod1 ? routput : (output>>3);
if (!ismod1) {
chip->output_ch[(chip->cycles+1)%9] = chip->ch_out;
}
}
static void OPLL_DoRhythm(opll_t *chip) {

View File

@ -191,6 +191,8 @@ typedef struct {
int16_t output_m;
int16_t output_r;
int16_t output_ch[9];
} opll_t;
const opll_patch_t* OPLL_GetPatchROM(uint32_t chip_type);

View File

@ -245,6 +245,11 @@ void YMPSG_Write(ympsg_t *chip, uint8_t data)
chip->write_flag = 1;
}
void YMPSG_WriteStereo(ympsg_t *chip, uint8_t data)
{
chip->stereo = data;
}
uint16_t YMPSG_Read(ympsg_t *chip)
{
uint16_t data = 0;
@ -265,6 +270,7 @@ void YMPSG_Init(ympsg_t *chip, uint8_t real_sn, uint8_t noise_tap1, uint8_t nois
chip->noise_tap1 = noise_tap1;
chip->noise_tap2 = noise_tap2;
chip->noise_size = noise_size;
chip->stereo = 0xff;
for (i = 0; i < 17; i++)
{
chip->vol_table[i]=(real_sn?tipsg_vol[i]:ympsg_vol[i]) * 8192.0f;
@ -316,32 +322,62 @@ void YMPSG_Clock(ympsg_t *chip)
}
}
int YMPSG_GetOutput(ympsg_t *chip)
void YMPSG_GetOutput(ympsg_t *chip, int* left, int* right)
{
int sample = 0;
int sample_left = 0;
int sample_right = 0;
uint32_t i;
YMPSG_UpdateSample(chip);
if (chip->test & 1)
{
sample += chip->vol_table[chip->volume_out[chip->test >> 1]];
sample += chip->vol_table[16] * 3;
sample_left += chip->vol_table[chip->volume_out[chip->test >> 1]];
sample_left += chip->vol_table[16] * 3;
sample_right += chip->vol_table[chip->volume_out[chip->test >> 1]];
sample_right += chip->vol_table[16] * 3;
}
else if (!chip->mute)
{
sample += chip->vol_table[chip->volume_out[0]];
sample += chip->vol_table[chip->volume_out[1]];
sample += chip->vol_table[chip->volume_out[2]];
sample += chip->vol_table[chip->volume_out[3]];
if (chip->stereo&(0x10)) {
sample_left += chip->vol_table[chip->volume_out[0]];
}
if (chip->stereo&(0x01)) {
sample_right += chip->vol_table[chip->volume_out[0]];
}
if (chip->stereo&(0x20)) {
sample_left += chip->vol_table[chip->volume_out[1]];
}
if (chip->stereo&(0x02)) {
sample_right += chip->vol_table[chip->volume_out[1]];
}
if (chip->stereo&(0x40)) {
sample_left += chip->vol_table[chip->volume_out[2]];
}
if (chip->stereo&(0x04)) {
sample_right += chip->vol_table[chip->volume_out[2]];
}
if (chip->stereo&(0x80)) {
sample_left += chip->vol_table[chip->volume_out[3]];
}
if (chip->stereo&(0x08)) {
sample_right += chip->vol_table[chip->volume_out[3]];
}
}
else
{
for (i = 0; i < 4; i++)
{
if (!((chip->mute>>i) & 1))
sample += chip->vol_table[chip->volume_out[i]];
if (!((chip->mute>>i) & 1)) {
if (chip->stereo&(0x10<<i)) {
sample_left += chip->vol_table[chip->volume_out[i]];
}
if (chip->stereo&(0x01<<i)) {
sample_right += chip->vol_table[chip->volume_out[i]];
}
}
}
}
return sample;
*left=sample_left;
*right=sample_right;
}
void YMPSG_Test(ympsg_t *chip, uint16_t test)
@ -349,7 +385,7 @@ void YMPSG_Test(ympsg_t *chip, uint16_t test)
chip->test = (test >> 9) & 7;
}
/*
void YMPSG_Generate(ympsg_t *chip, int32_t *buf)
{
uint32_t i;
@ -373,7 +409,7 @@ void YMPSG_Generate(ympsg_t *chip, int32_t *buf)
}
out = YMPSG_GetOutput(chip);
*buf = (int32_t)(out * 8192.f);
}
}*/
void YMPSG_WriteBuffered(ympsg_t *chip, uint8_t data)
{

View File

@ -52,6 +52,8 @@ typedef struct {
uint8_t test;
uint8_t volume_out[4];
uint8_t stereo;
//
uint64_t writebuf_samplecnt;
uint32_t writebuf_cur;
@ -68,15 +70,16 @@ typedef struct {
void YMPSG_Write(ympsg_t *chip, uint8_t data);
void YMPSG_WriteStereo(ympsg_t *chip, uint8_t data);
uint16_t YMPSG_Read(ympsg_t *chip);
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn, uint8_t noise_tap1, uint8_t noise_tap2, uint32_t noise_size);
void YMPSG_SetIC(ympsg_t *chip, uint32_t ic);
void YMPSG_Clock(ympsg_t *chip);
int YMPSG_GetOutput(ympsg_t *chip);
void YMPSG_GetOutput(ympsg_t *chip, int* left, int* right);
void YMPSG_Test(ympsg_t *chip, uint16_t test);
void YMPSG_Generate(ympsg_t *chip, int32_t *buf);
//void YMPSG_Generate(ympsg_t *chip, int32_t *buf);
void YMPSG_WriteBuffered(ympsg_t *chip, uint8_t data);
void YMPSG_SetMute(ympsg_t *chip, uint8_t mute);

View File

@ -12,6 +12,8 @@
#include <stddef.h>
#include <functional>
#include <string>
#include <vector>
/* denotes UTF-8 char */
typedef char nfdchar_t;
@ -35,19 +37,19 @@ typedef enum {
/* nfd_<targetplatform>.c */
/* single file open dialog */
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
nfdresult_t NFD_OpenDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback = NULL );
/* multiple file open dialog */
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
nfdresult_t NFD_OpenDialogMultiple( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdpathset_t *outPaths,
nfdselcallback_t selCallback = NULL );
/* save dialog */
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
nfdresult_t NFD_SaveDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback = NULL );

View File

@ -14,7 +14,7 @@
//
// might as well make Objective-Ruswift++...
static NSArray *BuildAllowedFileTypes( const char *filterList )
static NSArray *BuildAllowedFileTypes( const std::vector<std::string>& filterList )
{
// Commas and semicolons are the same thing on this platform
@ -22,32 +22,28 @@ static NSArray *BuildAllowedFileTypes( const char *filterList )
// NSMutableArray *buildFilterList = NSMutableArray::alloc()->init();
NSMutableArray *buildFilterList = [[NSMutableArray alloc] init];
char typebuf[NFD_MAX_STRLEN] = {0};
size_t filterListLen = strlen(filterList);
char *p_typebuf = typebuf;
for ( size_t i = 0; i < filterListLen+1; ++i )
{
if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' )
{
if (filterList[i] != '\0')
++p_typebuf;
*p_typebuf = '\0';
// or this: NSString::stringWithUTF8String(typebuf);
// buildFilterList->addObject(thisType);
// really? did you have to make this mess?!
NSString *thisType = [NSString stringWithUTF8String: typebuf];
[buildFilterList addObject:thisType];
p_typebuf = typebuf;
*p_typebuf = '\0';
}
else
{
*p_typebuf = filterList[i];
++p_typebuf;
std::string typebuf;
for (const std::string& i: filterList) {
typebuf="";
for (const char& j: i) {
if (j==' ' || j==',' || j ==';') {
// or this: NSString::stringWithUTF8String(typebuf);
// buildFilterList->addObject(thisType);
// really? did you have to make this mess?!
const char* typebufC=typebuf.c_str();
NSString *thisType = [NSString stringWithUTF8String:typebufC];
[buildFilterList addObject:thisType];
typebuf="";
} else if (j!='.' && j!='*') {
typebuf+=j;
}
}
if (!typebuf.empty()) {
// I don't think this will work, but come on...
const char* typebufC=typebuf.c_str();
NSString *thisType = [NSString stringWithUTF8String:typebufC];
[buildFilterList addObject:thisType];
}
}
NSArray *returnArray = [NSArray arrayWithArray:buildFilterList];
@ -56,9 +52,9 @@ static NSArray *BuildAllowedFileTypes( const char *filterList )
return returnArray;
}
static void AddFilterListToDialog( NSSavePanel *dialog, const char *filterList )
static void AddFilterListToDialog( NSSavePanel *dialog, const std::vector<std::string>& filterList )
{
if ( !filterList || strlen(filterList) == 0 )
if ( filterList.size()&1 )
return;
NSArray *allowedFileTypes = BuildAllowedFileTypes( filterList );
@ -130,7 +126,7 @@ static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset )
/* public */
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
nfdresult_t NFD_OpenDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback )
@ -173,7 +169,7 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
}
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
nfdresult_t NFD_OpenDialogMultiple( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdpathset_t *outPaths,
nfdselcallback_t selCallback )
@ -218,7 +214,7 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
}
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
nfdresult_t NFD_SaveDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback )

View File

@ -197,46 +197,22 @@ static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr )
#endif
}
/* ext is in format "jpg", no wildcards or separators */
static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t specBufLen )
{
const char SEP[] = ";";
assert( specBufLen > strlen(ext)+3 );
if ( strlen(specBuf) > 0 )
{
strncat( specBuf, SEP, specBufLen - strlen(specBuf) - 1 );
specBufLen += strlen(SEP);
}
char extWildcard[NFD_MAX_STRLEN];
int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext );
assert( bytesWritten == (int)(strlen(ext)+2) );
_NFD_UNUSED(bytesWritten);
strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 );
return NFD_OKAY;
}
static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList )
static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const std::vector<std::string>& filterList )
{
const wchar_t WILDCARD[] = L"*.*";
if ( !filterList || strlen(filterList) == 0 )
if (filterList.empty())
return NFD_OKAY;
// Count rows to alloc
UINT filterCount = 1; /* guaranteed to have one filter on a correct, non-empty parse */
const char *p_filterList;
for ( p_filterList = filterList; *p_filterList; ++p_filterList )
{
if ( *p_filterList == ';' )
++filterCount;
}
// list size has to be an even number (name/filter)
if (filterList.size()&1)
return NFD_ERROR;
// Count rows to alloc
UINT filterCount = filterList.size()>>1; /* guaranteed to have one filter on a correct, non-empty parse */
if (filterCount==0) filterCount=1;
assert(filterCount);
if ( !filterCount )
{
NFDi_SetError("Error parsing filters.");
@ -244,62 +220,39 @@ static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char
}
/* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */
COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * ((size_t)filterCount + 1) );
COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * ((size_t)filterCount) );
if ( !specList )
{
return NFD_ERROR;
}
for (UINT i = 0; i < filterCount+1; ++i )
for (UINT i = 0; i < filterCount; ++i )
{
specList[i].pszName = NULL;
specList[i].pszSpec = NULL;
}
size_t specIdx = 0;
p_filterList = filterList;
char typebuf[NFD_MAX_STRLEN] = {0}; /* one per comma or semicolon */
char *p_typebuf = typebuf;
char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */
for (size_t i=0; i<filterList.size(); i+=2) {
String name=filterList[i];
String spec=filterList[i+1];
for (char& i: spec) {
if (i==' ') i=';';
}
if (spec==".*") spec="*.*";
while ( 1 )
{
if ( NFDi_IsFilterSegmentChar(*p_filterList) )
{
/* append a type to the specbuf (pending filter) */
AppendExtensionToSpecBuf( typebuf, specbuf, NFD_MAX_STRLEN );
p_typebuf = typebuf;
memset( typebuf, 0, sizeof(char)*NFD_MAX_STRLEN );
}
if ( *p_filterList == ';' || *p_filterList == '\0' )
{
/* end of filter -- add it to specList */
CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszName );
CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszSpec );
memset( specbuf, 0, sizeof(char)*NFD_MAX_STRLEN );
++specIdx;
if ( specIdx == filterCount )
break;
}
if ( !NFDi_IsFilterSegmentChar( *p_filterList ))
{
*p_typebuf = *p_filterList;
++p_typebuf;
}
++p_filterList;
CopyNFDCharToWChar( name.c_str(), (wchar_t**)&specList[specIdx].pszName );
CopyNFDCharToWChar( spec.c_str(), (wchar_t**)&specList[specIdx].pszSpec );
++specIdx;
}
/* Add wildcard */
specList[specIdx].pszSpec = WILDCARD;
specList[specIdx].pszName = WILDCARD;
/* Add wildcard if specIdx is 0 */
if (specIdx==0) {
specList[specIdx].pszSpec = WILDCARD;
specList[specIdx].pszName = WILDCARD;
}
fileOpenDialog->SetFileTypes( filterCount+1, specList );
fileOpenDialog->SetFileTypes( filterCount, specList );
/* free speclist */
for ( size_t i = 0; i < filterCount; ++i )
@ -450,7 +403,7 @@ static nfdresult_t SetDefaultPath( IFileDialog *dialog, const char *defaultPath
/* public */
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
nfdresult_t NFD_OpenDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback )
@ -558,7 +511,7 @@ end:
return nfdResult;
}
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
nfdresult_t NFD_OpenDialogMultiple( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdpathset_t *outPaths,
nfdselcallback_t selCallback )
@ -653,7 +606,7 @@ end:
return nfdResult;
}
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
nfdresult_t NFD_SaveDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback )

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More