private static string GetPointerOffset(int packNumber) { //This method calculates where the pack pointer is, so we can make CoilSnake overwrite //it with whatever ROM offset the contents of the CCScript file gets inserted into! //A documentation of instrument packs can be found here: //https://gist.github.com/vince94/cb70ddd4c5309c0c52e662f985d6648b //The disassembly shows this pointer table here: //https://github.com/Herringway/ebsrc/blob/master/src/data/music/pack_pointer_table.asm //Note to self - the pointer data at these locations have swapped bytes. //A pointer that reads E2F077 in a hex editor is pointing to E277F0. //This is reflected in the CCScript command, which swaps things around before writing to the ROM. var initialOffset = 0x04F947; var sizeOfEachPointer = 3; var i = 0; var result = initialOffset; while (i < packNumber) { result += sizeOfEachPointer; i++; } result = HexHelpers.OffsetConvert_PCtoHiROM(result); return($"0x{result:X6}"); }
public byte[] MakeHex() { var result = new List <byte>(); result.AddRange(HexHelpers.UInt16toByteArray_LittleEndian(aramOffset)); result.AddRange(HexHelpers.UInt16toByteArray_LittleEndian(loopOffset)); return(result.ToArray()); }
public static ushort DecodeLoopPoint(byte[] fileData) { //This function takes in an AddMusicK-format BRR file and returns its loop point's numerical value. //Special thanks to Milon in the SMW Central discord for this explanation. //A BRR loop point is stored by taking the raw value, dividing it by 16, and multiplying it by 9. //For example, Chrono Trigger's Choir sample has a loop point of 2288. //2288 / 16 = 143 //143 * 9 = 1287 //1287 in hex is [05 07], which is then swapped as [07 05]. //(Which is what you see if you open that file in a hex editor!) //So to do the inverse, we need to take the first two bytes of a file, //reverse them, divide the number by 9, and multiply it by 16. var amkLoopHeader = IsolateLoopPoint(fileData); return((ushort)(HexHelpers.ByteArrayToInt(amkLoopHeader) / 9 * 16)); }
internal static List <BRRFile> LoadBRRs(string folderPath) { var allFiles = Directory.GetFiles(GetFullPath(folderPath)); var brrFileList = LoadFilenames(folderPath); //Get just the filenames from config.txt var dupeIndex = 0; //Find matches based on the list, add them to the result var result = new List <BRRFile>(); foreach (var nameToLookFor in brrFileList) { if (nameToLookFor.Contains("0x")) { //this line is a duplicate, not a reference to a file - it contains manual ARAM offsets var offsets = nameToLookFor.Split("0x"); result.Add(new BRRFile { dupeStartOffset = HexHelpers.HexStringToUInt16(offsets[1]), dupeLoopOffset = HexHelpers.HexStringToUInt16(offsets[2]), data = new byte[0], //empty so the running offset count doesn't get incremented filename = $"Duplicate #{dupeIndex} {nameToLookFor}", //it breaks if there's identical filenames here }); dupeIndex++; } else { foreach (string filename in allFiles) { bool alreadyInserted = false; var info = new FileInfo(filename); if (info.Extension != ".brr" || info.Name != nameToLookFor) { continue; //skip everything that isn't a BRR file, and files that aren't the next one in the list } foreach (var foo in result) { if (foo.filename.Contains(nameToLookFor)) { alreadyInserted = true; } } if (alreadyInserted) { continue; } var fileContents = File.ReadAllBytes(info.FullName); if (BRRFunctions.FileHasNoLoopHeader(fileContents.Length)) { result.Add(new BRRFile { data = fileContents, loopPoint = 0, //rudimentary support for raw, non-AMK BRRs filename = info.Name, }); } else { //separate the loop point header and the actual BRR data, and add them to the list result.Add(new BRRFile { data = BRRFunctions.IsolateBRRdata(fileContents), loopPoint = BRRFunctions.DecodeLoopPoint(fileContents), filename = info.Name, }); } } } } return(result); }
internal static List <Patch> LoadMetadata(string folderPath) { //rip through the textfile var lines = File.ReadLines(GetFullConfigFilepath(folderPath)); byte instIndex = ARAM.defaultFirstSampleIndex; //set instIndex to the default value before even checking var dupeIndex = 0; var result = new List <Patch>(); foreach (var line in lines) { var tempLine = line; if (tempLine.Contains(';')) { tempLine = tempLine.Split(';')[0].Trim(); //get rid of comments if there are any } if (tempLine.ToLower().Contains(BASE_INSTRUMENT)) { if (tempLine.Contains(OVERWRITE)) //change instIndex if "default" isn't in the textfile after all { instIndex = 0x00; } else if (!tempLine.Contains(DEFAULT)) { var splitLine = line.Split(": ")[1]; instIndex = HexHelpers.HexStringToByte(splitLine); } } if (LineShouldBeSkipped(line)) { continue; } var lineContents = CleanTextFileLine(tempLine); if (lineContents[0].Contains("0x")) { //if the Patch filename isn't the same as the BRR filename, glitches happen lineContents[0] = $"Duplicate #{dupeIndex} {lineContents[0]}"; dupeIndex++; } var temp = new Patch { index = instIndex, ADSR1 = byte.Parse(lineContents[1], NumberStyles.HexNumber), ADSR2 = byte.Parse(lineContents[2], NumberStyles.HexNumber), Gain = byte.Parse(lineContents[3], NumberStyles.HexNumber), Multiplier = byte.Parse(lineContents[4], NumberStyles.HexNumber), Sub = byte.Parse(lineContents[5], NumberStyles.HexNumber), Filename = lineContents[0] }; result.Add(temp); instIndex++; } return(result); }
internal static Config LoadConfig(string folderPath) { byte tempPackNum = 0xFF; byte tempBaseInst = 0xFF; ushort tempBRRoffset = 0xFFFF; //load the first three lines of the config.txt var lines = File.ReadLines(GetFullConfigFilepath(folderPath)).ToList(); for (int i = 0; i < 3; i++) { var line = lines[i].ToLower().Split(":"); if (line[0].Contains(PACK_NUMBER)) { if (line[1].Contains(DEFAULT)) { Program.GracefulCrash("Please specify a pack number in your config.txt!"); } else { tempPackNum = HexHelpers.HexStringToByte(line[1]); } } else if (line[0].Contains(BASE_INSTRUMENT)) { if (line[1].Contains(DEFAULT)) { tempBaseInst = 0x1A; } else if (line[1].Contains(OVERWRITE)) { tempBaseInst = 0x00; } else { tempBaseInst = HexHelpers.HexStringToByte(line[1]); } } else if (line[0].Contains(SAMPLE_OFFSET)) { if (line[1].Contains(DEFAULT)) { tempBRRoffset = ARAM.samplesOffset_1A; } else if (line[1].Contains(OVERWRITE)) { tempBRRoffset = ARAM.samplesOffset; } else { tempBRRoffset = HexHelpers.HexStringToUInt16(line[1]); } } } //load patches here var tempPatches = LoadMetadata(folderPath); var result = new Config(GetFolderName(folderPath), tempPackNum, tempBaseInst, tempBRRoffset, tempPatches); //Check that everything's there if (result.offsetForSampleDir == 0xFFFF) { Program.GracefulCrash("Couldn't find a value for Sample Directory Offset in config.txt!"); } else if (result.offsetForBRRdump == 0xFFFF) { Program.GracefulCrash("Couldn't find a value for BRR Sample Dump Offset in config.txt!"); } else if (result.offsetForInstrumentConfig == 0xFFFF) { Program.GracefulCrash("Couldn't find a value for Instrument Config Table Offset in config.txt!"); } else if (result.packNumber == 0xFF) { Program.GracefulCrash("Couldn't find a value for Pack Number in config.txt!"); } return(result); }