From 8846337af40dcb3a8521326080e5ed6239e5c5a0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 22 Jun 2024 18:22:03 -0500 Subject: [PATCH] prepare. --- CMakeLists.txt | 2 + src/engine/engine.h | 3 + src/engine/fileOps/fileOpsCommon.cpp | 4 ++ src/engine/fileOps/fileOpsCommon.h | 2 +- src/engine/fileOps/it.cpp | 83 +++++++++++++++++++++++++++ src/engine/fileOps/tfm.cpp | 2 + src/engine/fileOps/xm.cpp | 86 ++++++++++++++++++++++++++++ src/gui/gui.cpp | 2 +- 8 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 src/engine/fileOps/it.cpp create mode 100644 src/engine/fileOps/xm.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 198b52a1e..618e68e2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -672,10 +672,12 @@ src/engine/fileOps/dmf.cpp src/engine/fileOps/fc.cpp src/engine/fileOps/ftm.cpp src/engine/fileOps/fur.cpp +src/engine/fileOps/it.cpp src/engine/fileOps/mod.cpp src/engine/fileOps/s3m.cpp src/engine/fileOps/text.cpp src/engine/fileOps/tfm.cpp +src/engine/fileOps/xm.cpp src/engine/blip_buf.c src/engine/brrUtils.c diff --git a/src/engine/engine.h b/src/engine/engine.h index 3ee0768be..adf712b37 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -61,6 +61,9 @@ class DivWorkPool; #define DIV_VERSION_FC 0xff02 #define DIV_VERSION_S3M 0xff03 #define DIV_VERSION_FTM 0xff04 +#define DIV_VERSION_TFE 0xff05 +#define DIV_VERSION_XM 0xff06 +#define DIV_VERSION_IT 0xff07 enum DivStatusView { DIV_STATUS_NOTHING=0, diff --git a/src/engine/fileOps/fileOpsCommon.cpp b/src/engine/fileOps/fileOpsCommon.cpp index 38a324200..1a2f36319 100644 --- a/src/engine/fileOps/fileOpsCommon.cpp +++ b/src/engine/fileOps/fileOpsCommon.cpp @@ -154,9 +154,13 @@ bool DivEngine::load(unsigned char* f, size_t slen, const char* nameHint) { return loadFC(file,len); } else if (memcmp(file,DIV_TFM_MAGIC,8)==0) { return loadTFMv2(file,len); + } else if (memcmp(file,DIV_IT_MAGIC,4)==0) { + return loadIT(file,len); } else if (len>=48) { if (memcmp(&file[0x2c],DIV_S3M_MAGIC,4)==0) { return loadS3M(file,len); + } else if (memcmp(file,DIV_XM_MAGIC,17)==0) { + return loadXM(file,len); } } diff --git a/src/engine/fileOps/fileOpsCommon.h b/src/engine/fileOps/fileOpsCommon.h index 0ee24abc1..306166c55 100644 --- a/src/engine/fileOps/fileOpsCommon.h +++ b/src/engine/fileOps/fileOpsCommon.h @@ -53,7 +53,7 @@ struct NotZlibException { #define DIV_FC13_MAGIC "SMOD" #define DIV_FC14_MAGIC "FC14" #define DIV_S3M_MAGIC "SCRM" -#define DIV_XM_MAGIC "Extended Module:" +#define DIV_XM_MAGIC "Extended Module: " #define DIV_IT_MAGIC "IMPM" #define DIV_TFM_MAGIC "TFMfmtV2" diff --git a/src/engine/fileOps/it.cpp b/src/engine/fileOps/it.cpp new file mode 100644 index 000000000..78b29a571 --- /dev/null +++ b/src/engine/fileOps/it.cpp @@ -0,0 +1,83 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "fileOpsCommon.h" + +bool DivEngine::loadIT(unsigned char* file, size_t len) { + struct InvalidHeaderException {}; + bool success=false; + char magic[4]; + SafeReader reader=SafeReader(file,len); + warnings=""; + + try { + DivSong ds; + ds.version=DIV_VERSION_XM; + //ds.linearPitch=0; + //ds.pitchMacroIsLinear=false; + ds.noSlidesOnFirstTick=true; + ds.rowResetsArpPos=true; + ds.ignoreJumpAtEnd=false; + ds.pitchSlideSpeed=12; + + logV("Impulse Tracker module"); + + // load here + if (!reader.seek(0,SEEK_SET)) { + logE("premature end of file!"); + lastError="incomplete file"; + delete[] file; + return false; + } + reader.read(magic,4); + + if (memcmp(magic,DIV_IT_MAGIC,4)!=0) { + logW("the magic isn't complete"); + throw EndOfFileException(&reader,reader.tell()); + } + + ds.name=reader.readString(26); + + if (active) quitDispatch(); + BUSY_BEGIN_SOFT; + saveLock.lock(); + song.unload(); + song=ds; + changeSong(0); + recalcChans(); + saveLock.unlock(); + BUSY_END; + if (active) { + initDispatch(); + BUSY_BEGIN; + renderSamples(); + reset(); + BUSY_END; + } + success=true; + } catch (EndOfFileException& e) { + //logE("premature end of file!"); + lastError="incomplete file"; + } catch (InvalidHeaderException& e) { + //logE("invalid header!"); + lastError="invalid header!"; + } + return success; +} + diff --git a/src/engine/fileOps/tfm.cpp b/src/engine/fileOps/tfm.cpp index dfb35450d..282d3ed3d 100644 --- a/src/engine/fileOps/tfm.cpp +++ b/src/engine/fileOps/tfm.cpp @@ -521,6 +521,7 @@ bool DivEngine::loadTFMv1(unsigned char* file, size_t len) { try { DivSong ds; + ds.version=DIV_VERSION_TFE; ds.systemName="Sega Genesis/Mega Drive or TurboSound FM"; ds.subsong[0]->hz=50; ds.systemLen=1; @@ -712,6 +713,7 @@ bool DivEngine::loadTFMv2(unsigned char* file, size_t len) { try { DivSong ds; + ds.version=DIV_VERSION_TFE; ds.systemName="Sega Genesis/Mega Drive or TurboSound FM"; ds.subsong[0]->hz=50; ds.systemLen=1; diff --git a/src/engine/fileOps/xm.cpp b/src/engine/fileOps/xm.cpp new file mode 100644 index 000000000..e42362046 --- /dev/null +++ b/src/engine/fileOps/xm.cpp @@ -0,0 +1,86 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "fileOpsCommon.h" + +bool DivEngine::loadXM(unsigned char* file, size_t len) { + struct InvalidHeaderException {}; + bool success=false; + char magic[32]; + SafeReader reader=SafeReader(file,len); + warnings=""; + + try { + DivSong ds; + ds.version=DIV_VERSION_XM; + //ds.linearPitch=0; + //ds.pitchMacroIsLinear=false; + ds.noSlidesOnFirstTick=true; + ds.rowResetsArpPos=true; + ds.ignoreJumpAtEnd=false; + ds.pitchSlideSpeed=12; + + logV("Extended Module"); + + // load here + if (!reader.seek(0,SEEK_SET)) { + logE("premature end of file!"); + lastError="incomplete file"; + delete[] file; + return false; + } + reader.read(magic,17); + + if (memcmp(magic,DIV_XM_MAGIC,17)!=0) { + logW("invalid magic"); + throw EndOfFileException(&reader,reader.tell()); + } + + ds.name=reader.readString(20); + + // 0x1a + reader.readC(); + + if (active) quitDispatch(); + BUSY_BEGIN_SOFT; + saveLock.lock(); + song.unload(); + song=ds; + changeSong(0); + recalcChans(); + saveLock.unlock(); + BUSY_END; + if (active) { + initDispatch(); + BUSY_BEGIN; + renderSamples(); + reset(); + BUSY_END; + } + success=true; + } catch (EndOfFileException& e) { + //logE("premature end of file!"); + lastError="incomplete file"; + } catch (InvalidHeaderException& e) { + //logE("invalid header!"); + lastError="invalid header!"; + } + return success; +} + diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 6a49cc2f6..7e8a9fe85 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1669,7 +1669,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirSong)) workingDirSong=getHomeDir(); hasOpened=fileDialog->openLoad( _("Open File"), - {_("compatible files"), "*.fur *.dmf *.mod *.s3m *.fc13 *.fc14 *.smod *.fc *.ftm *.0cc *.dnm *.eft *.fub *.tfe", + {_("compatible files"), "*.fur *.dmf *.mod *.s3m *.xm *.it *.fc13 *.fc14 *.smod *.fc *.ftm *.0cc *.dnm *.eft *.fub *.tfe", _("all files"), "*"}, workingDirSong, dpiScale