public new static Asset Load(string assetPath)
        {
            byte[] buff = File.ReadAllBytes(assetPath);
            var    res  = JsonSerializer.Deserialize <AssetSprite>(buff, ProjectFile.JsonOptions);

            using (SHA1Managed sha1 = new SHA1Managed())
            {
                res.Length = buff.Length;

                string dir = Path.GetDirectoryName(assetPath);

                int    ind      = 0;
                string basePath = Path.Combine(dir, res.Name);
                string pngPath  = basePath + "_" + ind.ToString() + ".png";
                while (File.Exists(pngPath))
                {
                    // Load PNG file, make new texture item for it
                    Bitmap imgBitmap;
                    using (var temp = new Bitmap(pngPath))
                        imgBitmap = temp.FullCopy();
                    res.TextureItems.Add(new GMTextureItem(imgBitmap));

                    byte[] imgBuff = imgBitmap.GetReadOnlyByteArray();
                    sha1.TransformBlock(imgBuff, 0, imgBuff.Length, null, 0);
                    res.Length += imgBuff.Length;

                    pngPath = basePath + "_" + (++ind).ToString() + ".png";
                }

                // Load special info buffer
                if (res.SpecialInfo?.Buffer != null)
                {
                    string path = Path.Combine(dir, res.SpecialInfo.Buffer);
                    if (File.Exists(path))
                    {
                        byte[] specialBuff = File.ReadAllBytes(path);
                        res.SpecialInfo.InternalBuffer = new BufferRegion(specialBuff);

                        sha1.TransformBlock(specialBuff, 0, specialBuff.Length, null, 0);
                        res.Length += specialBuff.Length;
                    }
                }

                // Load raw collision masks
                if ((int)res.CollisionMask.Mode < 0)
                {
                    res.CollisionMask.RawMasks = new List <BufferRegion>();

                    ind     = 0;
                    pngPath = basePath + "_mask_" + ind.ToString() + ".png";
                    while (File.Exists(pngPath))
                    {
                        Bitmap imgBitmap;
                        using (var temp = new Bitmap(pngPath))
                            imgBitmap = temp.FullCopy();

                        byte[] mask = CollisionMasks.GetMaskFromImage(imgBitmap);
                        res.CollisionMask.RawMasks.Add(new BufferRegion(mask));

                        sha1.TransformBlock(mask, 0, mask.Length, null, 0);
                        res.Length += mask.Length;

                        pngPath = basePath + "_mask_" + (++ind).ToString() + ".png";
                    }
                }

                sha1.TransformFinalBlock(buff, 0, buff.Length);
                res.Hash = sha1.Hash;
            }

            return(res);
        }
Beispiel #2
0
        public override void ConvertData(ProjectFile pf, int index)
        {
            GMSprite asset = (GMSprite)pf.Sprites[index].DataAsset;

            AssetSprite projectAsset = new AssetSprite()
            {
                Name         = asset.Name?.Content,
                Transparent  = asset.Transparent,
                Smooth       = asset.Smooth,
                Preload      = asset.Preload,
                Width        = asset.Width,
                Height       = asset.Height,
                OriginX      = asset.OriginX,
                OriginY      = asset.OriginY,
                TextureItems = asset.TextureItems.ToList()
            };

            projectAsset.TextureItems.RemoveAll(i => i == null);

            // Determine texture group
            if (projectAsset.TextureItems.Count == 0)
            {
                projectAsset.TextureGroup = null;
            }
            else
            {
                var group = pf.Textures.TextureGroups[
                    pf.Textures.PageToGroup[projectAsset.TextureItems[0].TexturePageID]];

                // If this group only has this sprite, and also has a page for
                // each item, then this is a separate group
                if (new HashSet <GMTextureItem>(group.Items).SetEquals(projectAsset.TextureItems) &&
                    group.Pages.Count == projectAsset.TextureItems.Count)
                {
                    projectAsset.SeparateTextureGroup = true;
                }

                projectAsset.TextureGroup = group.Name;
            }

            // Determine collision mask info
            List <Bitmap> bitmaps;

            projectAsset.CollisionMask = CollisionMasks.GetInfoForSprite(pf, asset, out bitmaps);
            List <BufferRegion> regenerated = CollisionMasks.GetMasksForSprite(pf, projectAsset, out _, bitmaps);

            if (!CollisionMasks.CompareMasks(asset.CollisionMasks, regenerated))
            {
                bool manual = true;
                if (projectAsset.CollisionMask.Type == AssetSprite.CollisionMaskInfo.MaskType.Diamond ||
                    projectAsset.CollisionMask.Type == AssetSprite.CollisionMaskInfo.MaskType.Ellipse)
                {
                    // This may be a false positive diamond/ellipse, try suggesting Precise
                    projectAsset.CollisionMask = CollisionMasks.GetInfoForSprite(pf, asset, out bitmaps, true);
                    regenerated = CollisionMasks.GetMasksForSprite(pf, projectAsset, out _, bitmaps);
                    manual      = !CollisionMasks.CompareMasks(asset.CollisionMasks, regenerated);
                }

                if (manual)
                {
                    // Need to generate manually
                    projectAsset.CollisionMask.Mode           = (AssetSprite.CollisionMaskInfo.MaskMode)(-1 - (int)projectAsset.CollisionMask.Mode);
                    projectAsset.CollisionMask.AlphaTolerance = null;
                    projectAsset.CollisionMask.Left           = asset.MarginLeft;
                    projectAsset.CollisionMask.Top            = asset.MarginTop;
                    projectAsset.CollisionMask.Right          = asset.MarginRight;
                    projectAsset.CollisionMask.Bottom         = asset.MarginBottom;
                    projectAsset.CollisionMask.RawMasks       = asset.CollisionMasks;
                }
            }

            // Fix incorrect sizes resulting from mods:
            //  - If a texture entry's source width/height are greater than bound width/height, scale the bound width/height
            //  - Check for largest bound width/height, max them with sprite width/height, store in sprite width/height and all bound width/heights
            bool resized = false;
            int  largestWidth = projectAsset.Width, largestHeight = projectAsset.Height;

            foreach (var item in projectAsset.TextureItems)
            {
                if (item == null)
                {
                    continue;
                }

                if (item.SourceWidth > item.BoundWidth)
                {
                    item.BoundWidth = item.SourceWidth;
                }
                if (item.SourceHeight > item.BoundHeight)
                {
                    item.BoundHeight = item.SourceHeight;
                }

                largestWidth  = Math.Max(largestWidth, item.BoundWidth);
                largestHeight = Math.Max(largestHeight, item.BoundHeight);
            }

            if (largestWidth > projectAsset.Width)
            {
                resized            = true;
                projectAsset.Width = largestWidth;
            }
            if (largestHeight > projectAsset.Height)
            {
                resized             = true;
                projectAsset.Height = largestHeight;
            }

            if (resized)
            {
                foreach (var item in projectAsset.TextureItems)
                {
                    if (item == null)
                    {
                        continue;
                    }

                    item.BoundWidth  = (ushort)projectAsset.Width;
                    item.BoundHeight = (ushort)projectAsset.Height;
                }

                if ((int)projectAsset.CollisionMask.Mode < 0)
                {
                    // Can't use the raw data; it's a different size
                    projectAsset.CollisionMask.Mode     = (AssetSprite.CollisionMaskInfo.MaskMode)(-(1 + (int)projectAsset.CollisionMask.Mode));
                    projectAsset.CollisionMask.RawMasks = null;
                }
            }

            // Now just handle special data
            if (asset.SpecialOrGMS2)
            {
                projectAsset.SpecialInfo = new AssetSprite.SpriteSpecialInfo()
                {
                    SpriteType = asset.S_SpriteType
                };

                if (asset.S_SpriteType != GMSprite.SpriteType.Normal)
                {
                    projectAsset.SpecialInfo.Buffer         = "buffer.bin";
                    projectAsset.SpecialInfo.InternalBuffer = asset.S_Buffer;
                }

                if (pf.DataHandle.VersionInfo.IsNumberAtLeast(2))
                {
                    projectAsset.SpecialInfo.GMS2PlaybackSpeed     = asset.GMS2_PlaybackSpeed;
                    projectAsset.SpecialInfo.GMS2PlaybackSpeedType = asset.GMS2_PlaybackSpeedType;

                    if (asset.GMS2_3_Sequence != null)
                    {
                        var seq = asset.GMS2_3_Sequence.Sequence;

                        List <AssetSprite.SpriteSpecialInfo.SequenceInfo.Frame> frames = new();
                        foreach (var keyframe in
                                 (seq.Tracks[0].Keyframes as GMSequence.Track.ValueKeyframes).List)
                        {
                            frames.Add(new()
                            {
                                Position = keyframe.Key,
                                Length   = keyframe.Length,
                                Index    = keyframe.Channels.Values.First().Value
                            });
                        }

                        List <AssetSprite.SpriteSpecialInfo.SequenceInfo.BroadcastMessage> broadcastMessages = new();
                        foreach (var message in seq.BroadcastMessages)
                        {
                            broadcastMessages.Add(new()
                            {
                                Position = message.Key,
                                Message  = message.Channels.Values.First().List.First().Content
                            });
                        }

                        projectAsset.SpecialInfo.Sequence = new AssetSprite.SpriteSpecialInfo.SequenceInfo()
                        {
                            Name              = seq.Name?.Content,
                            Frames            = frames,
                            BroadcastMessages = broadcastMessages
                        };
                    }

                    if (asset.GMS2_3_2_NineSlice != null)
                    {
                        projectAsset.SpecialInfo.NineSlice = new AssetSprite.SpriteSpecialInfo.NineSliceInfo()
                        {
                            Left      = asset.GMS2_3_2_NineSlice.Left,
                            Top       = asset.GMS2_3_2_NineSlice.Top,
                            Right     = asset.GMS2_3_2_NineSlice.Right,
                            Bottom    = asset.GMS2_3_2_NineSlice.Bottom,
                            Enabled   = asset.GMS2_3_2_NineSlice.Enabled,
                            TileModes = asset.GMS2_3_2_NineSlice.TileModes.ToList()
                        };
                    }
                }
            }

            pf.Sprites[index].Asset = projectAsset;
        }
        protected override byte[] WriteInternal(ProjectFile pf, string assetPath, bool actuallyWrite)
        {
            byte[] buff = JsonSerializer.SerializeToUtf8Bytes(this, ProjectFile.JsonOptions);

            string dir = null;

            if (actuallyWrite)
            {
                dir = Path.GetDirectoryName(assetPath);
                Directory.CreateDirectory(dir);
                using (FileStream fs = new FileStream(assetPath, FileMode.Create))
                    fs.Write(buff, 0, buff.Length);
            }

            // Compute hash manually here
            using (SHA1Managed sha1 = new SHA1Managed())
            {
                Length = buff.Length;

                // Handle sprite frames
                for (int i = 0; i < TextureItems.Count; i++)
                {
                    GMTextureItem item = TextureItems[i];

                    byte[] imgBuff;
                    Bitmap imgBitmap;
                    if (item.TexturePageID == -1)
                    {
                        imgBitmap = item._Bitmap;
                    }
                    else
                    {
                        imgBitmap = pf.Textures.GetTextureEntryBitmap(item, Width, Height);
                    }
                    imgBuff = imgBitmap.GetReadOnlyByteArray();

                    if (actuallyWrite)
                    {
                        using (FileStream fs = new FileStream(Path.Combine(dir, Name + "_" + i.ToString() + ".png"), FileMode.Create))
                            imgBitmap.Save(fs, System.Drawing.Imaging.ImageFormat.Png);
                    }

                    Length += imgBuff.Length;
                    sha1.TransformBlock(imgBuff, 0, imgBuff.Length, null, 0);
                }

                if (SpecialInfo != null)
                {
                    if (SpecialInfo.InternalBuffer != null &&
                        SpecialInfo.Buffer != null)
                    {
                        byte[] internalBufferArray = SpecialInfo.InternalBuffer.Memory.ToArray();

                        if (actuallyWrite)
                        {
                            using (FileStream fs = new FileStream(Path.Combine(dir, SpecialInfo.Buffer), FileMode.Create))
                                fs.Write(internalBufferArray, 0, internalBufferArray.Length);
                        }

                        Length += internalBufferArray.Length;
                        sha1.TransformBlock(internalBufferArray, 0, internalBufferArray.Length, null, 0);
                    }
                }

                // Save raw collision masks
                if (CollisionMask.RawMasks?.Count > 0)
                {
                    for (int i = 0; i < CollisionMask.RawMasks.Count; i++)
                    {
                        byte[] mask = CollisionMask.RawMasks[i].Memory.ToArray();

                        if (actuallyWrite)
                        {
                            using (FileStream fs = new FileStream(Path.Combine(dir, Name + "_mask_" + i.ToString() + ".png"), FileMode.Create))
                                CollisionMasks.GetImageFromMask(Width, Height, mask).Save(fs, System.Drawing.Imaging.ImageFormat.Png);
                        }

                        Length += mask.Length;
                        sha1.TransformBlock(mask, 0, mask.Length, null, 0);
                    }
                }

                sha1.TransformFinalBlock(buff, 0, buff.Length);
                Hash = sha1.Hash;
            }
            return(null);
        }
Beispiel #4
0
        public override void ConvertProject(ProjectFile pf)
        {
            var dataAssets = pf.DataHandle.GetChunk <GMChunkSPRT>().List;

            // Assemble dictionary of group names to actual Group classes
            Dictionary <string, Textures.Group> groupNames = new Dictionary <string, Textures.Group>();

            foreach (var g in pf.Textures.TextureGroups)
            {
                groupNames[g.Name] = g;
            }

            List <GMSprite> newList = new List <GMSprite>();

            for (int i = 0; i < pf.Sprites.Count; i++)
            {
                AssetSprite projectAsset = pf.Sprites[i].Asset;
                if (projectAsset == null)
                {
                    // This asset was never converted
                    newList.Add((GMSprite)pf.Sprites[i].DataAsset);
                    continue;
                }

                // Add texture items to group
                if (!projectAsset.SeparateTextureGroup &&
                    projectAsset.TextureGroup != null &&
                    groupNames.TryGetValue(projectAsset.TextureGroup, out var group))
                {
                    foreach (var item in projectAsset.TextureItems)
                    {
                        if (item != null)
                        {
                            group.AddNewEntry(pf.Textures, item);
                        }
                    }
                }
                else
                {
                    if (projectAsset.SeparateTextureGroup)
                    {
                        // Export each frame to a separate texture page
                        foreach (var item in projectAsset.TextureItems)
                        {
                            if (item == null)
                            {
                                continue;
                            }

                            var g = new Textures.Group()
                            {
                                Dirty     = true,
                                Border    = 0,
                                AllowCrop = false,
                                Name      = $"__DS_AUTO_GEN_{projectAsset.Name}__{pf.Textures.TextureGroups.Count}",
                                FillTGIN  = false // Apparently
                            };
                            g.AddNewEntry(pf.Textures, item);
                            pf.Textures.TextureGroups.Add(g);
                        }
                    }
                    else
                    {
                        // Make a new texture group for this
                        var g = new Textures.Group()
                        {
                            Dirty     = true,
                            Border    = 0,
                            AllowCrop = false,
                            Name      = $"__DS_AUTO_GEN_{projectAsset.Name}__{pf.Textures.TextureGroups.Count}",
                            FillTGIN  = false // Apparently
                        };
                        foreach (var item in projectAsset.TextureItems)
                        {
                            if (item != null)
                            {
                                g.AddNewEntry(pf.Textures, item);
                            }
                        }
                        pf.Textures.TextureGroups.Add(g);
                    }
                }

                CollisionMasks.Rect outbbox;

                GMSprite dataAsset = new GMSprite()
                {
                    Name           = pf.DataHandle.DefineString(projectAsset.Name),
                    Transparent    = projectAsset.Transparent,
                    Smooth         = projectAsset.Smooth,
                    Preload        = projectAsset.Preload,
                    Width          = projectAsset.Width,
                    Height         = projectAsset.Height,
                    OriginX        = projectAsset.OriginX,
                    OriginY        = projectAsset.OriginY,
                    TextureItems   = new GMRemotePointerList <GMTextureItem>(),
                    CollisionMasks = CollisionMasks.GetMasksForSprite(pf, projectAsset, out outbbox)
                };

                // Get collision mask info
                var colInfo = projectAsset.CollisionMask;
                if (colInfo.Left == null || colInfo.Top == null || colInfo.Right == null || colInfo.Bottom == null)
                {
                    dataAsset.MarginLeft   = outbbox.Left;
                    dataAsset.MarginTop    = outbbox.Top;
                    dataAsset.MarginRight  = outbbox.Right;
                    dataAsset.MarginBottom = outbbox.Bottom;
                }
                else
                {
                    dataAsset.MarginLeft   = (int)colInfo.Left;
                    dataAsset.MarginTop    = (int)colInfo.Top;
                    dataAsset.MarginRight  = (int)colInfo.Right;
                    dataAsset.MarginBottom = (int)colInfo.Bottom;
                }

                if ((int)colInfo.Mode < 0)
                {
                    dataAsset.BBoxMode = (uint)(-(1 + (int)colInfo.Mode));
                    dataAsset.SepMasks = GMSprite.SepMaskType.Precise;
                }
                else
                {
                    dataAsset.BBoxMode = (uint)colInfo.Mode;
                    switch (colInfo.Type)
                    {
                    case AssetSprite.CollisionMaskInfo.MaskType.Rectangle:
                        dataAsset.SepMasks = GMSprite.SepMaskType.AxisAlignedRect;
                        break;

                    case AssetSprite.CollisionMaskInfo.MaskType.RectangleWithRotation:
                        dataAsset.SepMasks = GMSprite.SepMaskType.RotatedRect;
                        break;

                    case AssetSprite.CollisionMaskInfo.MaskType.Precise:
                    case AssetSprite.CollisionMaskInfo.MaskType.Diamond:
                    case AssetSprite.CollisionMaskInfo.MaskType.Ellipse:
                    case AssetSprite.CollisionMaskInfo.MaskType.PrecisePerFrame:
                        dataAsset.SepMasks = GMSprite.SepMaskType.Precise;
                        break;
                    }
                }

                // Actually add the texture items
                foreach (var item in projectAsset.TextureItems)
                {
                    dataAsset.TextureItems.Add(item);
                }

                if (projectAsset.SpecialInfo != null)
                {
                    var info = projectAsset.SpecialInfo;

                    dataAsset.SpecialOrGMS2 = true;
                    dataAsset.S_SpriteType  = info.SpriteType;
                    if (info.SpriteType != GMSprite.SpriteType.Normal)
                    {
                        dataAsset.S_Buffer = info.InternalBuffer;
                    }

                    if (info.GMS2PlaybackSpeed != null)
                    {
                        dataAsset.GMS2_PlaybackSpeed     = (float)info.GMS2PlaybackSpeed;
                        dataAsset.GMS2_PlaybackSpeedType = (GMSprite.AnimSpeedType)info.GMS2PlaybackSpeedType;
                    }

                    if (projectAsset.SpecialInfo.Sequence != null)
                    {
                        var seq    = projectAsset.SpecialInfo.Sequence;
                        var newSeq = new GMSequence()
                        {
                            Name              = pf.DataHandle.DefineString(seq.Name),
                            PlaybackType      = GMSequence.PlaybackTypeEnum.Loop,
                            PlaybackSpeed     = dataAsset.GMS2_PlaybackSpeed,
                            PlaybackSpeedType = dataAsset.GMS2_PlaybackSpeedType,
                            Length            = seq.Frames.Max(f => f.Position + f.Length),
                            OriginX           = dataAsset.OriginX,
                            OriginY           = dataAsset.OriginY,
                            Volume            = 1,
                            BroadcastMessages = new(),
                            Tracks            = new()
                            {
                                new()
                                {
                                    BuiltinName     = 0,
                                    IsCreationTrack = false,
                                    Keyframes       = new GMSequence.Track.ValueKeyframes()
                                    {
                                        List = new()
                                    },
                                    ModelName          = pf.DataHandle.DefineString("GMSpriteFramesTrack"),
                                    Name               = pf.DataHandle.DefineString("frames"),
                                    OwnedResources     = new(),
                                    OwnedResourceTypes = new(),
                                    Tags               = new(),
                                    Tracks             = new(),
                                    Traits             = GMSequence.Track.TraitsEnum.Unknown1
                                }
                            },