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);
        }