public static byte[] GenerateSampleDirectory(Config config, List <BRRFile> samples) { var currentAramOffset = config.offsetForBRRdump; //Thanks to BlueStone for helping with the following! var samplesByFilename = samples.ToDictionary(sample => sample.filename); var sampleDirectoriesByFilename = new Dictionary <string, SampleDirectory>(); var resultSampleDirectory = new List <SampleDirectory>(); foreach (var patch in config.patches) { // If an entry with the same filename was already added, reuse the existing entry. if (sampleDirectoriesByFilename.TryGetValue(patch.Filename, out SampleDirectory entry)) { resultSampleDirectory.Add(entry); } else { // We haven't added an entry with this file before, so find the sample and add the entry. if (!samplesByFilename.TryGetValue(patch.Filename, out BRRFile currentSample)) { if (!patch.Filename.Contains("0x")) //don't display this message for duplicates { Console.WriteLine("Sample file \"" + patch.Filename + "\" does not exist!"); } } else { ushort tempStartOffset = 0; //Duplicates need to use the explicitly-specified offsets ushort tempLoopOffset = 0; //instead of the running count that BRR files use if (currentSample.filename.Contains("Duplicate")) { tempStartOffset = currentSample.dupeStartOffset; tempLoopOffset = currentSample.dupeLoopOffset; } else { tempStartOffset = currentAramOffset; if (currentSample.loopPoint == 0) { tempLoopOffset = (ushort)(currentAramOffset + currentSample.data.Length); //loopPoint is 0, so unlooped samples would otherwise loop infinitely } else { tempLoopOffset = (ushort)(currentAramOffset + BRRFunctions.EncodeLoopPoint(currentSample.loopPoint)); } } var sampleDirEntry = new SampleDirectory { aramOffset = tempStartOffset, loopOffset = tempLoopOffset, Filename = currentSample.filename, }; sampleDirectoriesByFilename[patch.Filename] = sampleDirEntry; resultSampleDirectory.Add(sampleDirEntry); currentAramOffset += (ushort)currentSample.data.Length; } } } //turn the entire thing into a byte array var result = new List <byte>(); foreach (var entry in resultSampleDirectory) { result.AddRange(entry.MakeHex()); } return(result.ToArray()); }
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); }
//TODO: //Edge-case bugs that people find //Comission an icon for the program //Make it so it puts the BRRs in the CCScript file seperately and puts a comment with the filename //Recreate vanilla packs in the Examples folder to make them easier for people to modify //Finish songs instead of messing with this!!! static void Main(string[] args) { const bool DEBUG = false; string folderPath; Console.Title = "EarthBound Instrument Packer"; //load the folder contents if (DEBUG) { folderPath = @"C:\Users\vince\Dropbox\Programming scratchpad\EarthBound-Instrument-Packer\EBInstPack\Examples\famicomDetectiveClub"; } else { if (args.Length == 0) //If they just double-clicked the exe - no args present { Console.WriteLine("Command-line usage:"); Console.WriteLine(" EBInstPack [folder path in quotes]"); Console.WriteLine(" Or just drag the folder onto the EXE!"); Console.WriteLine(); Console.WriteLine("Input the folder path where the samples & text file are:"); folderPath = Console.ReadLine(); } else { folderPath = args[0]; //Use the command-line argument if it's present } if (FileIO.FolderNonexistant(folderPath)) { return; } } //load the config.txt var config = FileIO.LoadConfig(folderPath); var samples = FileIO.LoadBRRs(folderPath); //Generate all the hex var sampleDirectory = Config.GenerateSampleDirectory(config, samples); var brrDump = BRRFunctions.Combine(samples); //Validation var tooManyBRRs = ARAM.CheckBRRLimit(brrDump, config.offsetForBRRdump); config.maxDelay = ARAM.GetMaxDelayPossible(brrDump, config.offsetForBRRdump); Console.WriteLine($"Highest possible delay value for this pack: {config.maxDelay:X2}"); Console.WriteLine($"These BRRs will be loaded into ARAM from {config.offsetForBRRdump:X4} to {config.offsetForBRRdump + brrDump.Length:X4}.\n"); var ccsFile = CCScriptOutput.Generate(config, sampleDirectory, brrDump); FileIO.SaveCCScriptFile(ccsFile, folderPath, config); //Check the folder for .EBM files var ebmFiles = FileIO.LoadEBMs(folderPath); foreach (var song in ebmFiles) { var spc = new PreviewSPC(config, sampleDirectory, brrDump, song); FileIO.SaveSPCfile(spc.filedata, folderPath, song.name); } Console.WriteLine("Press any key to continue..."); Console.ReadKey(); }