Exemplo n.º 1
0
        /// <summary>
        /// Reads a project XML file and relinks all the parts for file rebuilding
        /// </summary>
        /// <param name="filePath">XML file to parse</param>
        public BNSAXMLFile(string filePath)
        {
            this.FilePath = filePath;
            Console.WriteLine("Loading project file: " + filePath);
            XmlDocument doc = new XmlDocument();

            doc.Load(filePath);

            //Parse Process:
            //1. Read animation tree
            //2. Read tileset folder
            //3. Read minianim folder
            //4. Read palette folder
            //5. Read oamdatalists folder
            //6. Link all objects

            //Build lists, mark pointer placeholders
            //Byte align after lists are built
            //insert pointers
            //merge into single file

            string tilesetBasepath      = Directory.GetParent(filePath).FullName + "\\tilesets\\tileset";
            string palettesBasepath     = Directory.GetParent(filePath).FullName + "\\palettes\\palette";
            string oamDataListsBasepath = Directory.GetParent(filePath).FullName + "\\oamdatalists\\oamdatalist";
            string miniAnimsBasepath    = Directory.GetParent(filePath).FullName + "\\minianims\\minianim";



            XmlNodeList animationNodes = doc.DocumentElement.SelectNodes("/bnsafile/animations/animation");

            Console.WriteLine("Found " + animationNodes.Count + " animations");
            Animations = new List <Animation>();
            foreach (XmlNode animationNode in animationNodes)
            {
                //Read Animation XML
                Animation animation = new Animation(animationNode);
                //Verify references exist in filesystem
                foreach (Frame f in animation.Frames)
                {
                    //Verify Tileset
                    string tilesetPath = tilesetBasepath + f.TilesetIndex.ToString().PadLeft(3, '0') + ".bin";
                    if (!File.Exists(tilesetPath))
                    {
                        IsValid = false;
                        Console.WriteLine("Tileset file missing: " + tilesetPath + ", referenced by Anim " + animation.Index + " frame " + f.Index);
                        return;
                    }

                    //Verify OAM
                    string oamPath = oamDataListsBasepath + f.OAMDataListIndex + "-0-0.bin";
                    if (!File.Exists(oamPath))
                    {
                        IsValid = false;
                        Console.WriteLine("OAM Data List file missing: " + oamPath + ", referenced by Anim " + animation.Index + " frame " + f.Index);
                        return;
                    }

                    //Verify MiniAnim
                    string minianimFrame1 = miniAnimsBasepath + f.MiniAnimationIndex + "-0-0.bin";
                    if (!File.Exists(minianimFrame1))
                    {
                        IsValid = false;
                        Console.WriteLine("MiniAnimation file missing: " + minianimFrame1 + ", referenced by Anim " + animation.Index + " frame " + f.Index);
                        return;
                    }
                }
                Animations.Add(animation);
                Console.WriteLine("OK - animation contains all direct binary references");
            }
            //Read Tilesets
            Tilesets = new List <Tileset>();
            int index = 0;

            for (int i = 0; i < 999; i++)
            {
                string tilesetPath = tilesetBasepath + index.ToString().PadLeft(3, '0') + ".bin";
                if (File.Exists(tilesetPath))
                {
                    Tileset tileset = new Tileset(tilesetPath, i);
                    Tilesets.Add(tileset);
                }
                else
                {
                    break; //No more tilesets
                }
                index++;
            }
            Console.WriteLine("Read " + Tilesets.Count + " tilesets");

            //Read Palettes
            Palettes = new List <Palette>();
            for (int i = 0; i < 16; i++)
            {
                string palettePath = palettesBasepath + i.ToString().PadLeft(2, '0') + ".bin";
                if (File.Exists(palettePath))
                {
                    Palette palette = new Palette(palettePath);
                    Palettes.Add(palette);
                }
                else
                {
                    break; //No more palettes
                }
            }
            Console.WriteLine("Read " + Palettes.Count + " palettes");

            //Read MiniAnims
            string[] miniAnimFiles = Directory.GetFiles(Directory.GetParent(filePath).FullName + "\\minianims");
            int      maxindex      = 0;

            foreach (string file in miniAnimFiles)
            {
                string fname = Path.GetFileNameWithoutExtension(file);
                fname = fname.Substring(8);
                int dashIndex = fname.IndexOf('-');
                fname    = fname.Substring(0, dashIndex);
                maxindex = Math.Max(maxindex, Int32.Parse(fname));
            }
            Console.WriteLine("Found " + (maxindex + 1) + " mini animations");
            MiniAnimationGroups = new List <MiniAnimGroup>();
            for (index = 0; index <= maxindex; index++)
            {
                int subindex = 0;
                Console.WriteLine("Parsing Group " + index);
                MiniAnimGroup group = new MiniAnimGroup(index);
                while (File.Exists(miniAnimsBasepath + maxindex + "-" + subindex + "-0.bin"))
                {
                    //MiniAnimation List
                    MiniAnim animation = new MiniAnim(miniAnimsBasepath, index, subindex);
                    group.MiniAnimations.Add(animation);
                    subindex++;
                }
                MiniAnimationGroups.Add(group);
            }

            //Read OAM Data
            string[] oamListEntries = Directory.GetFiles(Directory.GetParent(filePath).FullName + "\\oamdatalists");
            maxindex = 0;
            foreach (string file in oamListEntries)
            {
                string fname = Path.GetFileNameWithoutExtension(file);
                fname = fname.Substring(11);
                int dashIndex = fname.IndexOf('-');
                fname    = fname.Substring(0, dashIndex);
                maxindex = Math.Max(maxindex, Int32.Parse(fname));
            }
            Console.WriteLine("Found " + (maxindex + 1) + " OAM Data List Groups");
            OAMDataListGroups = new List <OAMDataListGroup>();
            for (int i = 0; i <= maxindex; i++)
            {
                Console.WriteLine("Parsing OAM Data List Group " + i);
                OAMDataListGroup list = new OAMDataListGroup(oamDataListsBasepath, i);
                OAMDataListGroups.Add(list);
            }
            IsValid = true;
        }
        private void loadBNSA(Stream bnsaStream)
        {
            //HEADER
            LargestTileset = bnsaStream.ReadByte();
            //Magic Number Test
            if (bnsaStream.ReadByte() != 0 || bnsaStream.ReadByte() != 1) //0x1 and 0x2 positions
            {
                //Invalid Magic Number
                ValidBNSA = false;
                return;
            }
            AnimationCount = bnsaStream.ReadByte();
            Console.WriteLine("Number of Animations: " + AnimationCount);
            //Read Animation Pointers
            for (int i = 0; i < AnimationCount; i++)
            {
                int       animationPointer = ReadIntegerFromStream(bnsaStream);
                long      nextPosition     = bnsaStream.Position;
                Animation animation        = new Animation(this, animationPointer, bnsaStream);
                Animations.Add(animation);
                if (i < AnimationCount - 1)
                {
                    bnsaStream.Seek(nextPosition, SeekOrigin.Begin); //reset position to next pointer
                }
            }

            //Read Tilesets
            TilesetStartPointer = bnsaStream.Position;
            Console.WriteLine("Reading Tilesets, starting at 0x" + TilesetStartPointer.ToString("X2"));
            int index = 0;

            while (bnsaStream.Position < ProbablyPaletteStartPointer)
            {
                //Read Tilesets
                Tileset ts = new Tileset(bnsaStream, index);
                Tilesets.Add(ts); //Might need some extra checking...
                index++;
            }

            //Read Palettes
            PaletteStartPointer = bnsaStream.Position;
            Console.WriteLine("Found start of Palettes at 0x" + PaletteStartPointer.ToString("X2"));
            if (ReadIntegerFromStream(bnsaStream) == 0x20)
            {
                while (true)
                {
                    long pos = bnsaStream.Position;
                    //Verify next item is a palette and not a minianim
                    int minianimCheckPtr1 = ReadIntegerFromStream(bnsaStream);
                    if (minianimCheckPtr1 % 4 == 0)
                    {
                        //All pointers in the minianim table are lined up on a 4-byte boundary.
                        //Indexing starts at 0. Each pointer is 4 bytes, so the first one will have to point to a 4-point value

                        //could be an pointer to 1-frame minianim, maybe...
                        if (minianimCheckPtr1 == 4)
                        {
                            long subpos = bnsaStream.Position;
                            //Check for 1 pointer minianim signature
                            if (bnsaStream.ReadByte() == 0x0 && bnsaStream.ReadByte() == 0x01 && bnsaStream.ReadByte() == 0x80)
                            {
                                bnsaStream.Seek(pos, SeekOrigin.Begin);
                                break; //Not a palette. Next Item is a single-frame minianim group. End of Palette Block
                            }
                            else
                            {
                                bnsaStream.Seek(subpos, SeekOrigin.Begin); //rewind and continue check
                            }
                        }

                        //Could be a multi-miniframe mugshot, let's check the amount of pointers and the values.
                        Boolean determinedDataIsPalette  = false;
                        int     numberOfMiniAnimsInGroup = minianimCheckPtr1 / 4; //Pointer will go to first value after pointer table, so divide by num bytes in a pointer
                        if (minianimCheckPtr1 == 0 || numberOfMiniAnimsInGroup * 4 > bnsaStream.Length - pos)
                        {
                            //couldn't possbibly be a minianim. Read it as a palette.
                            determinedDataIsPalette = true;
                        }

                        if (!determinedDataIsPalette)
                        {
                            int previousPointerToCheckAgainst = minianimCheckPtr1;

                            Boolean validMiniAnimPointerData = true;
                            for (int i = 1; i < numberOfMiniAnimsInGroup; i++)
                            {
                                int nextMiniAnimCheckPtr = ReadIntegerFromStream(bnsaStream);
                                if (nextMiniAnimCheckPtr < previousPointerToCheckAgainst)
                                {
                                    validMiniAnimPointerData = false;
                                    break;
                                }
                            }
                            if (validMiniAnimPointerData)
                            {
                                //Probably a minianim
                                bnsaStream.Seek(pos, SeekOrigin.Begin);
                                break;
                            }
                        } //end of check for readAsPalette
                    }
                    bnsaStream.Seek(pos, SeekOrigin.Begin);
                    Console.WriteLine("Reading Palette 0x" + bnsaStream.Position.ToString("X2"));
                    Palette palette = new Palette(bnsaStream);
                    OriginalPalettes.Add(palette);
                    //Console.WriteLine("Reading next Palette 0x" + bnsaStream.Position.ToString("X2"));
                }
            }//} else
             //{
             //    Console.WriteLine("Palettes should start here, but we didn't find 0x00000020! (At position  0x" + PaletteStartPointer.ToString("X2") + ")");
             //    return;
             //}

            //Read Mini-Animations
            Console.WriteLine("Reading MiniAnim Data at 0x" + bnsaStream.Position.ToString("X2"));
            while (true)
            {
                //Validate next data is a mini animation.
                long          groupStartPos = bnsaStream.Position;
                MiniAnimGroup group         = new MiniAnimGroup(bnsaStream);
                if (!group.IsValid)
                {
                    //End of Mini-Anims
                    bnsaStream.Seek(groupStartPos, SeekOrigin.Begin);
                    break;
                }
                MiniAnimGroups.Add(group);
                //Round up to the next 4 byte boundary
                bnsaStream.ReadByte(); //pos++
                while (bnsaStream.Position % 4 != 0)
                {
                    bnsaStream.ReadByte(); //Official game padding. Since we are assuming these are all official, when repacking we should also follow this padding rule.
                }
            }



            //Read OAM Data Block Lists
            Console.WriteLine("Reading OAM Data Blocks at 0x" + bnsaStream.Position.ToString("X2"));
            int oindex = 0;

            while (bnsaStream.Position < bnsaStream.Length)
            {
                byte firstByte = (byte)bnsaStream.ReadByte();
                bnsaStream.Seek(-1, SeekOrigin.Current);
                if (firstByte == 0xFF)
                {
                    break; //End marker.
                }
                OAMDataListGroup oamListBlock = new OAMDataListGroup(bnsaStream, oindex);
                OAMDataListGroups.Add(oamListBlock);
                //Round up to the next 4 byte boundary (if possible)
                if (bnsaStream.Position < bnsaStream.Length)
                {
                    bnsaStream.ReadByte(); //pos++
                    while (bnsaStream.Position % 4 != 0 && bnsaStream.Position < bnsaStream.Length)
                    {
                        bnsaStream.ReadByte(); //Official game padding. Since we are assuming these are all official, when repacking we should also follow this padding rule.
                    }
                }
                else
                {
                    Console.WriteLine("Not rounding up boundary because at or past end of stream.");
                }
                oindex++;
            }

            if (bnsaStream.Position != bnsaStream.Length)
            {
                Console.WriteLine("...Nothing left to parse but we aren't at the end of the file!");
            }
            else
            {
                Console.WriteLine("...Reached end of the file.");
            }
        }
        /// <summary>
        /// Converts pointers to references to other parsed BNSA objects. This allows us to export references rather than hard coded values for easy recompilation and editing of the outputted XML.
        /// </summary>
        /// <param name="parsedBNSA">BNSA File that has been parsed</param>
        public void ResolveReferences(BNSAFile parsedBNSA)
        {
            //foreach (Palette palette in parsedBNSA.Palettes)
            //{
            //    if (palette.Pointer == PalettePointer)
            //    {
            //        ResolvedPalette = palette;
            //        break;
            //    }
            //}

            foreach (Tileset tileset in parsedBNSA.Tilesets)
            {
                if (tileset.Pointer == TilesetPointer)
                {
                    ResolvedTileset = tileset;
                    break;
                }
            }
            foreach (OAMDataListGroup oamDataList in parsedBNSA.OAMDataListGroups)
            {
                if (oamDataList.Pointer == OAMDataListPointer)
                {
                    ResolvedOAMDataListGroup = oamDataList;
                    break;
                }
            }
            foreach (MiniAnimGroup minianimgroup in parsedBNSA.MiniAnimGroups)
            {
                if (minianimgroup.Pointer == MiniAnimationPointer)
                {
                    ResolvedMiniAnimGroup = minianimgroup;
                    ResolvedMiniAnimGroup.ResolveReferences(parsedBNSA, this);
                    break;
                }
            }



            //if (ResolvedPalette != null)
            //{
            //    Console.WriteLine("----Resolved Palette Reference");
            //}
            //else
            //{
            //    Console.WriteLine("----/!\\ Failed to Resolve Palette: 0x"+PalettePointer.ToString("X2"));
            //}

            if (ResolvedTileset != null)
            {
                Console.WriteLine("----Resolved Tileset Reference");
            }
            else
            {
                Console.WriteLine("----/!\\ Failed to Resolve Tileset Pointer 0x" + TilesetPointer.ToString("X2"));
                throw new Exception("Failed to resolve mini-animation object.");
            }

            if (ResolvedMiniAnimGroup != null)
            {
                Console.WriteLine("----Resolved MiniAnim Reference");
            }
            else
            {
                Console.WriteLine("----/!\\ Failed to Resolve MiniAnim 0x" + MiniAnimationPointer.ToString("X2"));
                throw new Exception("Failed to resolve mini-animation object.");
            }

            if (ResolvedOAMDataListGroup != null)
            {
                Console.WriteLine("----Resolved OAM Data List Reference");
            }
            else
            {
                Console.WriteLine("----/!\\ Failed to Resolve OAM Data List 0x" + OAMDataListPointer.ToString("X2"));
                throw new Exception("Failed to resolve mini-animation object.");
            }



            //foreach (MiniAnim tileset in parsedBNSA.MiniAnimGroups)
            //{
            //    if (minianim.Pointer == TilesetPointer)
            //    {
            //        ResolvedMiniAnim = tileset;
            //        break;
            //    }
            //}
        }