Skip to content

Commit

Permalink
Add DMF patch importer
Browse files Browse the repository at this point in the history
  • Loading branch information
superctr committed Feb 5, 2022
1 parent 0ad6c22 commit 66010f7
Show file tree
Hide file tree
Showing 6 changed files with 5,196 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ $(OBJ)/%.o: $(SRC)/%.cpp
@mkdir -p $(@D)
$(CXX) $(CFLAGS) -MMD -c $< -o $@

$(OBJ)/%.o: $(SRC)/%.c
@mkdir -p $(@D)
$(CXX) $(CFLAGS) -MMD -c $< -o $@

#======================================================================

MMLGUI_BIN = $(BIN)/mmlgui
Expand All @@ -55,6 +59,8 @@ MMLGUI_OBJS = \
$(OBJ)/audio_manager.o \
$(OBJ)/emu_player.o \
$(OBJ)/config_window.o \
$(OBJ)/miniz.o \
$(OBJ)/dmf_importer.o \

LDFLAGS_MMLGUI := $(LDFLAGS_IMGUI) $(LDFLAGS_CTRMML) $(LDFLAGS_LIBVGM)

Expand Down
208 changes: 208 additions & 0 deletions src/dmf_importer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#include <unistd.h>

#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>

#include "dmf_importer.h"
#include "stringf.h"

#define MINIZ_HEADER_FILE_ONLY
#include "miniz.c"

Dmf_Importer::Dmf_Importer(const char* filename)
{
if(std::ifstream is{filename, std::ios::binary | std::ios::ate})
{
auto size = is.tellg();
std::vector<uint8_t> dmfz(size, '\0');
is.seekg(0);
if(is.read((char*)&dmfz[0],size))
{
uLong dmfsize = 0x1000000;
data.resize(dmfsize, 0);

int res;
res = uncompress((Byte*) data.data(), &dmfsize, (Byte*) dmfz.data(), dmfz.size());
if (res != Z_OK)
{
error_output = "File could not be decompressed\n";
}
else
{
parse();
}
}
}
else
{
error_output = "File could not be opened\n";
}
}

std::string Dmf_Importer::get_error()
{
return error_output;
}

std::string Dmf_Importer::get_mml()
{
return mml_output;
}

static inline uint32_t read_le32(uint8_t* data)
{
return data[0]|(data[1]<<8)|(data[2]<<16)|(data[3]<<24);
}

static inline std::string read_str(uint8_t* data, uint8_t length)
{
std::string out;
for(int i = 0; i < length; i++)
{
out.push_back(*data++);
}
return out;
}

void Dmf_Importer::parse()
{
uint8_t tmp8;

uint8_t* dmfptr = data.data();

dmfptr += 16; // skip header

uint8_t version = *dmfptr++;
if(version != 0x18)
{
error_output = stringf("Incompatible DMF version (found '0x%02x', expected '0x18')\nTry opening and saving the file in Deflemask legacy.",version);
return;
}

uint8_t system = *dmfptr++;
if((system & 0x3f) != 0x02)
{
error_output = "Sorry, the DMF must be a GENESIS module.\n";
return;
}

channel_count = 10;
if(system & 0x40)
channel_count += 3;

// Skip song name
tmp8 = *dmfptr++;
dmfptr += tmp8;

// Skip song author
tmp8 = *dmfptr++;
dmfptr += tmp8;

// Skip highlight A/B, timebase, frame mode, custom HZ
dmfptr += 10;

pattern_rows = read_le32(dmfptr);
dmfptr += 4;
//printf("Number of pattern rows: %d (%08x)\n", dmf_pattern_rows, dmf_pattern_rows);

matrix_rows = *dmfptr++;
//printf("Number of pattern matrix rows: %d\n", dmf_matrix_rows);

// Skip pattern matrix rows
dmfptr += channel_count * matrix_rows;

// Now read instruments
instrument_count = *dmfptr++;

for(int ins_counter = 0; ins_counter < instrument_count; ins_counter ++)
{
tmp8 = *dmfptr++;
auto name = read_str(dmfptr, tmp8);
dmfptr += tmp8;

uint8_t type = *dmfptr++;

if(type == 0)
{
dmfptr = parse_psg_instrument(dmfptr, ins_counter, name);
}
else if(type == 1)
{
dmfptr = parse_fm_instrument(dmfptr, ins_counter, name);
}
else
{
error_output = "Encountered an unknown instrument type.\n";
return;
}
}
}

#define ALG ptr[0]
#define FB ptr[1]
#define AR ptr[op+1]
#define DR ptr[op+2]
#define ML ptr[op+3]
#define RR ptr[op+4]
#define SL ptr[op+5]
#define TL ptr[op+6]
#define KS ptr[op+8]
#define DT dt_table[ptr[op+9]]
#define SR ptr[op+10]
#define SSG (ptr[op+11] | ptr[op+0] * 100)

uint8_t* Dmf_Importer::parse_fm_instrument(uint8_t* ptr, int id, std::string str)
{
uint8_t op_table[4] = {4, 28, 16, 40};
uint8_t dt_table[7] = {7, 6, 5, 0, 1, 2, 3};

mml_output += stringf("@%d fm %d %d ; %s\n", id, ALG, FB, str.c_str());
for(int opr=0; opr<4; opr++)
{
uint8_t op = op_table[opr];
mml_output += stringf(" %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d \n",
AR, DR, SR, RR, SL, TL, KS, ML, DT, SSG);
}
return ptr + 52;
}

uint8_t* Dmf_Importer::parse_psg_instrument(uint8_t* ptr, int id, std::string str)
{
uint8_t size;
uint8_t loop;

size = *ptr++;
if(size)
{
mml_output += stringf("@%d psg ; %s", id, str.c_str());
loop = ptr[size * 4];

for(int i=0; i<size; i++)
{
mml_output += stringf( "%s %s%d",
(i % 16 == 0) ? "\n" : "",
(loop == i) ? "| " : "",
ptr[i * 4]);
}
mml_output += "\n";
ptr += size * 4 + 1;
}

for(int i = 0; i < 3; i++)
{
size = *ptr++;
if(size)
ptr += size * 4 + 1;
// skip arp macro mode
if(i == 0)
ptr++;
}

return ptr;
}
31 changes: 31 additions & 0 deletions src/dmf_importer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef DMF_IMPORTER_H
#define DMF_IMPORTER_H

#include <vector>
#include <string>
#include <cstdint>

class Dmf_Importer
{
public:
Dmf_Importer(const char* filename);

std::string get_error();
std::string get_mml();

private:
void parse();
uint8_t* parse_fm_instrument(uint8_t* ptr, int id, std::string str);
uint8_t* parse_psg_instrument(uint8_t* ptr, int id, std::string str);

std::string error_output;
std::string mml_output;
std::vector<uint8_t> data;

uint8_t channel_count;
uint8_t pattern_rows;
uint8_t matrix_rows;
uint8_t instrument_count;

};
#endif
34 changes: 34 additions & 0 deletions src/editor_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include "track_view_window.h"
#include "track_list_window.h"

#include "dmf_importer.h"

#include "imgui.h"

#include <GLFW/glfw3.h>
Expand All @@ -41,6 +43,7 @@ enum Flags
IGNORE_WARNING = 1<<7,
RECOMPILE = 1<<8,
EXPORT = 1<<9,
IMPORT = 1<<10,
};

Editor_Window::Editor_Window()
Expand Down Expand Up @@ -91,6 +94,8 @@ void Editor_Window::display()
if (ImGui::MenuItem("Save As...", "Ctrl+Alt+S"))
set_flag(SAVE|SAVE_AS|DIALOG);
ImGui::Separator();
if (ImGui::MenuItem("Import patches from DMF...", nullptr, nullptr, !editor.IsReadOnly()))
set_flag(IMPORT|DIALOG);
show_export_menu();
ImGui::Separator();
if (ImGui::MenuItem("Close", "Ctrl+W"))
Expand Down Expand Up @@ -489,6 +494,21 @@ void Editor_Window::handle_file_io()
clear_flag(EXPORT);
}
}
else if(test_flag(IMPORT) && !modal_open)
{
modal_open = 1;
fs.chooseFileDialog(test_flag(DIALOG), fs.getLastDirectory(), ".dmf");
clear_flag(DIALOG);
if(strlen(fs.getChosenPath()) > 0)
{
import_file(fs.getChosenPath());
clear_flag(IMPORT);
}
else if(fs.hasUserJustCancelledDialog())
{
clear_flag(IMPORT);
}
}
}

int Editor_Window::load_file(const char* fn)
Expand All @@ -510,6 +530,20 @@ int Editor_Window::load_file(const char* fn)
return -1;
}

int Editor_Window::import_file(const char* fn)
{
auto t = Dmf_Importer(fn);
player_error += t.get_error();
if (!player_error.size())
{
clear_flag(MODIFIED);
set_flag(FILENAME_SET|RECOMPILE);
editor.InsertText(t.get_mml());
return 0;
}
return -1;
}

int Editor_Window::save_file(const char* fn)
{
// If we don't set ios::binary, the runtime will
Expand Down
1 change: 1 addition & 0 deletions src/editor_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Editor_Window : public Window
int load_file(const char* fn);
int save_file(const char* fn);
void export_file(const char* fn);
int import_file(const char* fn);

std::string get_display_filename() const;
std::string get_export_filename() const;
Expand Down
Loading

0 comments on commit 66010f7

Please sign in to comment.