Пример #1
0
        /// <summary>
        /// Reads a landtable from a byte array
        /// </summary>
        /// <param name="source"></param>
        /// <param name="address"></param>
        /// <param name="imageBase"></param>
        /// <param name="format"></param>
        /// <param name="labels"></param>
        /// <returns></returns>
        public static LandTable Read(byte[] source, uint address, uint imageBase, LandtableFormat format, Dictionary <uint, string> labels)
        {
            string name = labels.ContainsKey(address) ? labels[address] : "landtable_" + address.ToString("X8");
            float  radius;
            LandtableAttributes attribs = 0;

            string identifier = GenerateIdentifier();

            List <LandEntry>          geometry = new();
            string                    geomName;
            List <LandEntryMotion>    anim = new();
            string                    animName;
            Dictionary <uint, Attach> attaches = new();
            string                    texName  = "";
            uint texListPtr;

            uint   tmpaddr;
            ushort geoCount = source.ToUInt16(address);

            switch (format)
            {
            case LandtableFormat.SA1:
            case LandtableFormat.SADX:
                short anicnt = source.ToInt16(address + 2);
                attribs = (LandtableAttributes)source.ToUInt32(address + 4);
                radius  = source.ToSingle(address + 8);

                tmpaddr = source.ToUInt32(address + 0xC);
                if (tmpaddr != 0)
                {
                    tmpaddr -= imageBase;
                    geomName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "collist_" + tmpaddr.ToString("X8");

                    for (int i = 0; i < geoCount; i++)
                    {
                        geometry.Add(LandEntry.Read(source, tmpaddr, imageBase, AttachFormat.BASIC, format, labels, attaches));
                        tmpaddr += 0x24;
                    }
                }
                else
                {
                    geomName = "collist_" + identifier;
                }

                tmpaddr = source.ToUInt32(address + 0x10);
                if (tmpaddr != 0)
                {
                    tmpaddr -= imageBase;
                    animName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "animlist_" + tmpaddr.ToString("X8");

                    for (int i = 0; i < anicnt; i++)
                    {
                        anim.Add(LandEntryMotion.Read(source, tmpaddr, imageBase, AttachFormat.BASIC, format == LandtableFormat.SADX, labels, attaches));
                        tmpaddr += LandEntryMotion.Size;
                    }
                }
                else
                {
                    animName = "animlist_" + identifier;
                }

                tmpaddr = source.ToUInt32(address + 0x14);
                if (tmpaddr != 0)
                {
                    tmpaddr -= imageBase;
                    texName  = source.GetCString(tmpaddr, Encoding.ASCII);
                }
                texListPtr = source.ToUInt32(address + 0x18);

                break;

            case LandtableFormat.SA2:
            case LandtableFormat.SA2B:
                AttachFormat atcFmt = format == LandtableFormat.SA2 ? AttachFormat.CHUNK : AttachFormat.GC;

                ushort visualCount = source.ToUInt16(address + 2);
                radius = source.ToSingle(address + 0xC);

                tmpaddr = source.ToUInt32(address + 0x10);
                if (tmpaddr != 0)
                {
                    tmpaddr -= imageBase;
                    geomName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "collist_" + tmpaddr.ToString("X8");

                    for (int i = 0; i < geoCount; i++)
                    {
                        geometry.Add(LandEntry.Read(source, tmpaddr, imageBase, i >= visualCount ? AttachFormat.BASIC : atcFmt, format, labels, attaches));
                        tmpaddr += 0x20;
                    }
                }
                else
                {
                    geomName = "collist_" + identifier;
                }

                animName = "animlist_" + identifier;

                tmpaddr = source.ToUInt32(address + 0x18);
                if (tmpaddr != 0)
                {
                    tmpaddr -= imageBase;
                    texName  = source.GetCString(tmpaddr, Encoding.ASCII);
                }
                texListPtr = source.ToUInt32(address + 0x1C);

                break;

            default:
                throw new InvalidDataException("Landtable format not valid");
            }

            return(new(geometry, anim, format)
            {
                Name = name,
                DrawDistance = radius,
                Attributes = attribs,
                GeoName = geomName,
                GeoAnimName = animName,
                TexListPtr = texListPtr,
                TextureFileName = texName
            });
        }
Пример #2
0
        public void ConvertToFormat(LandtableFormat newFormat, bool optimize, bool forceUpdate)
        {
            if (newFormat == Format && !forceUpdate)
            {
                return;
            }

            NJObject dummyModel = new();

            void convertAttaches(AttachFormat format, HashSet <Attach> attaches, Dictionary <Attach, Attach> attachMap, HashSet <LandEntry> landentries)
            {
                foreach (Attach atc in attaches)
                {
                    dummyModel.Attach = atc;
                    dummyModel.ConvertAttachFormat(format, optimize, false, forceUpdate);
                    attachMap.Add(atc, dummyModel.Attach);
                }

                if (format == AttachFormat.Buffer)
                {
                    return;
                }

                foreach (LandEntry le in landentries)
                {
                    le.Attach = attachMap[le.Attach];
                }
            }

            var newAtcFormat = newFormat switch
            {
                LandtableFormat.SA1 or LandtableFormat.SADX => AttachFormat.BASIC,
                LandtableFormat.SA2 => AttachFormat.CHUNK,
                LandtableFormat.SA2B => AttachFormat.GC,
                _ => AttachFormat.Buffer,
            };

            if (newFormat <= LandtableFormat.SADX || newFormat == LandtableFormat.Buffer)
            {
                HashSet <Attach> attaches = Geometry.Select(x => x.Attach).ToHashSet();
                convertAttaches(newAtcFormat, attaches, new(), new(Geometry));
            }
            else
            {
                if (Format <= LandtableFormat.SADX || Format == LandtableFormat.Buffer)
                {
                    // attaches that are used for rendering
                    HashSet <Attach> visualAttaches = new();

                    // Attaches that are used for collision
                    HashSet <Attach> collisionAttaches = new();

                    HashSet <LandEntry> visualLandEntries    = new();
                    HashSet <LandEntry> collisionLandEntries = new();

                    // For sa1/dx, of which a landentry can be used for both, collision and rendering
                    HashSet <LandEntry> hybridLandEntries = new();

                    foreach (LandEntry le in Geometry)
                    {
                        bool isCollision = le.SurfaceAttributes.IsCollision();
                        bool isVisual    = !isCollision || le.SurfaceAttributes.HasFlag(SurfaceAttributes.Visible);
                        // if its neither, we'll just keep it as an invisible visual model. just in case

                        if (isCollision)
                        {
                            collisionAttaches.Add(le.Attach);
                            collisionLandEntries.Add(le);
                        }

                        if (isVisual)
                        {
                            visualAttaches.Add(le.Attach);
                            visualLandEntries.Add(le);
                        }

                        if (isVisual && isCollision)
                        {
                            hybridLandEntries.Add(le);
                        }
                    }

                    visualLandEntries.RemoveWhere(x => hybridLandEntries.Contains(x));
                    collisionLandEntries.RemoveWhere(x => hybridLandEntries.Contains(x));

                    Dictionary <Attach, Attach> visualAttachMap = new();
                    convertAttaches(newAtcFormat, visualAttaches, visualAttachMap, visualLandEntries);

                    Dictionary <Attach, Attach> collisionAttachMap = new();
                    convertAttaches(AttachFormat.BASIC, collisionAttaches, collisionAttachMap, collisionLandEntries);

                    foreach (LandEntry le in hybridLandEntries)
                    {
                        // the copy will act as collision
                        LandEntry copy = le.ShallowCopy();
                        copy.Attach = collisionAttachMap[le.Attach];
                        Geometry.Add(copy);

                        le.Attach = visualAttachMap[le.Attach];
                    }
                }
                else // when converting between sa2 formats
                {
                    // the collision format for sa2 and sa2b is the same, no conversion needed
                    HashSet <LandEntry> visualGeometry = Geometry.Where(x => x.Attach.Format != AttachFormat.BASIC).ToHashSet();
                    HashSet <Attach>    attaches       = visualGeometry.Select(x => x.Attach).ToHashSet();
                    convertAttaches(newAtcFormat, attaches, new(), new(visualGeometry));
                }
            }

            Format = newFormat;
        }