diff --git a/README.md b/README.md index abc2189..66f5589 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ Made with the Juce 6 C++ framework under the GPL3 license Click [the Wiki tab](https://github.com/sauraen/seq64/wiki) for articles on SEQ64 compilation and use. +## .com? .mus? What is all this? Where do I load a ROM? + +You don't load a ROM in SEQ64 V2.0. `.com`, `.aseq`, and `.m64` are all the same thing (Music Macro Language binary). `.mus` is the text-based assembly language version of this same data. The rest of this README explains in more detail. If you are using an old-school toolchain which doesn't split the ROM into its constituent files, but where you just edit the whole ROM in one tool after another, use SEQ64 V1. + ## Background Certain Nintendo 64 games made by Nintendo's internal studios, including Super Mario 64, Zelda, and several others, use a music sequence format called Music Macro Language (formerly referred to as `Audioseq` after the name of the file containing the sequences in the OoT Debug ROM). This format can be thought of as a cross between MIDI and a scripting language such as Python: it contains instructions for things like playing notes, pitch bend, and selecting instruments, and also programming instructions like branches, loops, calls, memory I/O, and some basic variable manipulation. In games which use this format, not only are MIDI-like music sequences stored in this format, but the first sequence in each game handles sound effects. This sequence is a huge "program" written in this language which listens for messages from the game engine and then plays miniature sequences for each of these sound effects. diff --git a/Source/SeqEditor.cpp b/Source/SeqEditor.cpp index aae14ab..020c8c7 100644 --- a/Source/SeqEditor.cpp +++ b/Source/SeqEditor.cpp @@ -944,7 +944,7 @@ void SeqEditor::buttonClicked (juce::Button* buttonThatWasClicked) if(!abi.isValid()) return; if(!checkSeqPresence(false)) return; File f = File::getSpecialLocation(File::userHomeDirectory); //TODO SEQ64::readFolderProperty("romfolder"); - FileChooser box("Load .com/.aseq", f, "*.com;*.aseq", true); + FileChooser box("Load .com/.aseq", f, "*.com;*.aseq;*.m64;*.bin;*.seq", true); if(!box.browseForFileToOpen()) return; f = box.getResult(); seq.reset(new SeqFile(abi)); @@ -956,7 +956,7 @@ void SeqEditor::buttonClicked (juce::Button* buttonThatWasClicked) //[UserButtonCode_btnExportCom] -- add your button handler code here.. if(!checkSeqPresence(true)) return; File savelocation = File::getSpecialLocation(File::userHomeDirectory); //SEQ64::readFolderProperty("comfolder"); - FileChooser box("Save .com/.aseq", savelocation, "*.com;*.aseq", true); + FileChooser box("Save .com/.aseq", savelocation, "*.com;*.aseq;*.m64;*.bin;*.seq", true); if(!box.browseForFileToSave(true)) return; savelocation = box.getResult(); startSeqOperation(".com/.aseq export", &SeqFile::exportCom, savelocation); diff --git a/Source/SeqFile.cpp b/Source/SeqFile.cpp index a7e94e1..df63d40 100644 --- a/Source/SeqFile.cpp +++ b/Source/SeqFile.cpp @@ -57,6 +57,7 @@ Identifier SeqFile::idSectionName("sectionname"); Identifier SeqFile::idOldSectionIdx("oldsectionidx"); Identifier SeqFile::idSecDone("secdone"); Identifier SeqFile::idTicks("ticks"); +Identifier SeqFile::idLastDelay("lastdelay"); Identifier SeqFile::idLabelName("labelname"); Identifier SeqFile::idLabelNameAuto("labelnameauto"); Identifier SeqFile::idSrcCmdRef("srccmdref"); @@ -267,8 +268,7 @@ void SeqFile::getCommandRange(ValueTree command, String meaning, int &range_min, int SeqFile::getLargestCommandRange(int stype, String action, String meaning){ ValueTree test, param, param2; ValueTree possibleCmdsList("possiblecmdslist"); - bool flag; - int i, range, maxrange = 0; + int i, maxrange = 0; for(i=0; i= 0 && v <= max){ midiopts.setProperty(opt, v, nullptr); }else{ - dbgmsg(".pref: Invalid " + opt + " value " + value); + dbgmsg(".pref: Invalid " + opt + " value " + value + ": " + prefline); importresult |= 1; } } @@ -614,10 +614,10 @@ void SeqFile::prefSetHex(ValueTree midiopts, Identifier opt, int max, String val if(value.startsWithIgnoreCase("0x")) value = value.substring(2); else if(value.startsWith("$")) value = value.substring(1); int v = value.getHexValue32(); - if(isHex(value) && v >= 0 && v <= 255){ + if(isHex(value) && v >= 0 && v <= max){ midiopts.setProperty(opt, v, nullptr); }else{ - dbgmsg(".pref: Invalid " + opt + " value " + value); + dbgmsg(".pref: Invalid " + opt + " value " + value + ": " + prefline); importresult |= 1; } } @@ -1654,7 +1654,6 @@ void SeqFile::optimize(ValueTree midiopts){ int numCmdsDelete; // ValueTree want, want2, param; - int i; int cctype, cclast, ccdir; if(useLoops){ dbgmsg("\nLooking for data to loop"); @@ -3479,11 +3478,12 @@ int SeqFile::importMus(File musfile){ bool endSection = false, dontAdvanceLine = false; if(ln >= lines.size()){ if(section.hasProperty(idQuestionableSection)){ - line->Info("Ran into end of sequence while parsing unused code, " + dbgmsg("Ran into end of sequence while parsing unused code, " "calling that the end of the unused code"); }else{ - line->Warning("Ran into end of sequence while parsing normal code, " + dbgmsg("Ran into end of sequence while parsing normal code, " "normally this is an error but in some technical sequences it's OK"); + importresult |= 1; } dontAdvanceLine = true; endSection = true; @@ -3898,20 +3898,47 @@ String SeqFile::getSecNamePrefix(int dialect, ValueTree section){ return name; } +void SeqFile::registerExistingLabelName(ValueTree section){ + String name = section.getProperty(idLabelName).toString(); + if(alllabelnames.contains(name)){ + dbgmsg("Duplicate existing label name " + name + "!"); + importresult |= 2; + }else{ + alllabelnames.add(name); + } +} + +void SeqFile::setLabelNameAuto(ValueTree target, String name, String extra){ + if(extra.isEmpty()){ + dbgmsg("Internal error in setLabelNameAuto!"); + importresult |= 2; + return; + } + while(name.isEmpty() || alllabelnames.contains(name)){ + String name2 = name + extra; + //dbgmsg("Duplicate generated name " + name + ", changing to " + name2); + name = name2; + } + alllabelnames.add(name); + target.setProperty(idLabelName, name, nullptr); + target.setProperty(idLabelNameAuto, true, nullptr); +} + void SeqFile::nameSections(int dialect){ - StringArray allsecnames; //Main section ValueTree section = structure.getChild(0); - if(!section.hasProperty(idLabelName) || section.hasProperty(idLabelNameAuto)){ - String name = dialect >= 2 ? "_" + seqname : "_start"; - allsecnames.addIfNotAlreadyThere(name); - section.setProperty(idLabelName, name, nullptr); - section.setProperty(idLabelNameAuto, true, nullptr); + if(section.hasProperty(idLabelName) && !section.hasProperty(idLabelNameAuto)){ + registerExistingLabelName(section); + }else{ + setLabelNameAuto(section, dialect >= 2 ? "_" + seqname : "_start"); } //Rest of sections for(int sec=1; sec= 2 ? "pat" : "call") + section.getProperty(idSrcCmdRef).toString(); + name += "_" + String(dialect >= 2 ? "pat" : "call") + + section.getProperty(idSrcCmdRef).toString(); } - if(name.isEmpty() || allsecnames.contains(name)){ - //dbgmsg("Name clash for section " + String(sec) + " \"" + name + "\""); - name += "_" + String(sec); - } - allsecnames.addIfNotAlreadyThere(name); - section.setProperty(idLabelName, name, nullptr); - section.setProperty(idLabelNameAuto, true, nullptr); + setLabelNameAuto(section, name, "_" + String(sec)); } } @@ -3970,62 +3992,87 @@ void SeqFile::nameTargetCommands(int dialect){ ValueTree section = structure.getChild(sec); for(int cmd=0; cmd= 3) return 0; - sec.setProperty(idTicks, -1, nullptr); - int ticks = 0; - int lastnotedelay = -1; - for(int i=0; i= 0){ + if(starthash == 0 && sec.hasProperty(idTicks)){ + if(lastdelay != nullptr) *lastdelay = sec.getProperty(idLastDelay); + return sec.getProperty(idTicks); + } + int stype = sec.getProperty(idSType); + if(stype >= 3) return 0; + int ticks = 0; + int lastnotedelay = -1; + int i=0; + if(starthash != 0){ + ValueTree command = sec.getChildWithProperty(idHash, starthash); + if(!command.isValid()){ + dbgmsg("countTicks(): target hash not found! Sec " + + sec.getProperty(idLabelName).toString() + " hash " + + String(starthash)); + break; + } + i = sec.indexOf(command); + } + bool error = false; + for(; i &data, uint32_t address, int ValueTree param, desc; String action, meaning, datasrc; int i, len, paramlen, paramindex, cmdoffset, paramvalue, datalen; - uint8_t c, d; + uint8_t c; uint32_t a = address; // len = 1; diff --git a/Source/SeqFile.hpp b/Source/SeqFile.hpp index 107c037..90e1f22 100644 --- a/Source/SeqFile.hpp +++ b/Source/SeqFile.hpp @@ -172,13 +172,16 @@ class SeqFile{ bool getSectionAndCmd(ValueTree command, int &s, int &c); //For exportMus + StringArray alllabelnames; void assignTSection(ValueTree sec, int tsecnum); int assignAllTSections(); void generateTSecNames(int num_tsections, int dialect); String getSecNamePrefix(int dialect, ValueTree section); + void registerExistingLabelName(ValueTree section); + void setLabelNameAuto(ValueTree target, String name, String extra = "_"); void nameSections(int dialect); void nameTargetCommands(int dialect); - int countTicks(ValueTree sec); + int countTicks(ValueTree sec, int starthash, int *lastdelay); String getCommandMusLine(int sec, ValueTree section, ValueTree command, int dialect, int stype, int secticks); int findDynTableIndex(int sec); @@ -259,6 +262,7 @@ class SeqFile{ static Identifier idOldSectionIdx; static Identifier idSecDone; static Identifier idTicks; + static Identifier idLastDelay; static Identifier idLabelName; static Identifier idLabelNameAuto; static Identifier idSrcCmdRef;