/// <summary> /// Sets the instances /// </summary> private GMInstance[] SetInstances() { // Get regular and block instances List <GMareInstance> gmareInstances = new List <GMareInstance>(); gmareInstances.AddRange(App.Room.Instances.ToArray()); // Create a game maker instance array GMInstance[] instances = new GMInstance[gmareInstances.Count]; // Set room instances for (int i = 0; i < gmareInstances.Count; i++) { bool studio = _project.GameMakerVersion == GMVersionType.GameMakerStudio; GMObject obj = studio ? _project.Objects.Find(o => o.Name == gmareInstances[i].ObjectName) : _project.Objects.Find(o => o.Id == gmareInstances[i].ObjectId); // If the object does not exist in the target project, abort if (obj == null) { string objMessage = studio ? gmareInstances[i].ObjectName : gmareInstances[i].Name + "(Id: " + gmareInstances[i].ObjectId + ")"; MessageBox.Show("Could not find the object: " + objMessage + " within the target export project.", "GMare", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); return(null); } // Get the sprite for the instance offsets GMSprite sprite = studio ? _project.Sprites.Find(s => s.Name == obj.SpriteName) : _project.Sprites.Find(s => obj.SpriteId == obj.SpriteId); // If the sprite does not exist in the target project, abort if (sprite == null) { string spriteMessage = studio ? obj.SpriteName : obj.SpriteId.ToString(); MessageBox.Show("Could not find the sprite: " + spriteMessage + " within the target export project.", "GMare", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); return(null); } // Increment project last instance id _project.LastInstanceId++; // Create a new instance instances[i] = new GMInstance(); instances[i].Name = GetUniqueName(false); instances[i].CreationCode = (string)gmareInstances[i].CreationCode.Clone(); instances[i].Id = _project.LastInstanceId; instances[i].ObjectName = gmareInstances[i].ObjectName; instances[i].ObjectId = gmareInstances[i].ObjectId; instances[i].X = gmareInstances[i].X + sprite.OriginX; instances[i].Y = gmareInstances[i].Y + sprite.OriginY; instances[i].ScaleX = gmareInstances[i].ScaleX == 0 ? 1 : gmareInstances[i].ScaleX; instances[i].ScaleY = gmareInstances[i].ScaleY == 0 ? 1 : gmareInstances[i].ScaleY; instances[i].Rotation = gmareInstances[i].Rotation; } // Return an array of instances return(instances); }
public GMObject(BinaryReader binaryReader, GMWAD w) { Name = new GMString(binaryReader); SpriteIndex = binaryReader.ReadInt32(); Sprite = null; if (SpriteIndex > -1) { Sprite = w.Sprites.Items[SpriteIndex]; } Visible = ReadBool(binaryReader); Solid = ReadBool(binaryReader); Depth = binaryReader.ReadInt32(); Persistent = ReadBool(binaryReader); ParentIndex = binaryReader.ReadInt32(); Parent = null; MaskIndex = binaryReader.ReadInt32(); Mask = null; if (MaskIndex > -1) { Mask = w.Sprites.Items[MaskIndex]; } // It's a list with list with GMGMLEvents................... // yoyo nahooya tak hard blyat? int count = binaryReader.ReadInt32(); Debug.Assert(count == 12); // on PSP it should be 12. Events = new List <List <GMGMLEvent> >(count); long mprev_addr = binaryReader.BaseStream.Position; // Thanks to colinator27 for telling me how that works. for (int i = 0; i < count; i++) { uint e_addr = binaryReader.ReadUInt32(); long prev_addr = binaryReader.BaseStream.Position; binaryReader.BaseStream.Position = e_addr; int count2 = binaryReader.ReadInt32(); List <GMGMLEvent> l = new List <GMGMLEvent>(count2); for (int j = 0; j < count2; j++) { uint e_addr2 = binaryReader.ReadUInt32(); long prev_addr2 = binaryReader.BaseStream.Position; binaryReader.BaseStream.Position = e_addr2; int key = binaryReader.ReadInt32(); // subtype data???? var ev = new GMGMLEvent(binaryReader); ev.Key = key; l.Add(ev); binaryReader.BaseStream.Position = prev_addr2; } binaryReader.BaseStream.Position = prev_addr; Events.Add(l); } binaryReader.BaseStream.Position = mprev_addr; }
public static void Sprite(ProjectWriter writer, GMSprite self, GMProject _) { writer.Write(self.Name); writer.Write(self.Version); writer.Write(self.Origin); writer.Write(self.Subimages.Count); foreach (var subimage in self.Subimages) { writer.Write(800); writer.Write(subimage.Size); if (subimage.Width * subimage.Height != 0) { writer.Write(subimage, true); } } writer.Write(self.SeparateMasks); if (self.Subimages.Count <= 0) { return; } if (self.SeparateMasks) { foreach (var subimage in self.Subimages) { writer.Write(800); // mask data version writer.Write(subimage.Width); writer.Write(subimage.Height); writer.Write(self.BBox); int[] mask = CollisionMaskGen.Generate(subimage, self); foreach (int iii in mask) { writer.Write(iii); } } } else { writer.Write(800); // mask data version writer.Write(self.Subimages[0].Width); writer.Write(self.Subimages[0].Height); writer.Write(self.BBox); int[] mask = CollisionMaskGen.Generate(self.Subimages[0], self); foreach (int iii in mask) { writer.Write(iii); } } }
/// <summary> /// Gets objects /// </summary> /// <param name="file">The path to the Game Maker project file</param> public static bool GetObjects(string file) { // Create a new Game Maker project form using (ProjectIOForm form = new ProjectIOForm(file, true)) { // Show the form form.ShowDialog(); // If the project is empty, return if (form.GMProject == null || form.GMProject.ProjectTree == null || form.GMProject.ProjectTree.Nodes == null) { return(false); } // The object node index int objectIndex = -1; // Get the object node for (int i = 0; i < form.GMProject.ProjectTree.Nodes.Length; i++) { if (form.GMProject.ProjectTree.Nodes[i].ResourceType == GMResourceType.Objects) { objectIndex = i; } } // If no object node was found, return if (objectIndex == -1) { return(false); } // Check instances for differences CheckInstances(form.GMProject); // Clear previous objects if any App.Room.Objects.Clear(); // Iterate through project objects foreach (GMObject resource in form.GMProject.Objects) { // GMare object variables int originX = 0; int originY = 0; Bitmap image = null; // Get the sprite object GMSprite sprite = form.GMProject.Sprites.Find(i => i.Id == resource.SpriteId); // If a sprite was found if (sprite != null) { // Get sprite data originX = sprite.OriginX; originY = sprite.OriginY; // If there are no sub-images, skip image data if (sprite.SubImages.Length <= 0) { // Add new gmare object App.Room.Objects.Add(new GMareObject(resource, image, resource.SpriteId, resource.Depth, originX, originY)); continue; } image = GameMaker.Common.GMUtilities.GetBitmap(sprite.SubImages[0]); // Set transparency if needed if (sprite.Transparent && sprite.SubImages[0].Compressed) { image.MakeTransparent(image.GetPixel(0, image.Height - 1)); } } // Add new gmare object App.Room.Objects.Add(new GMareObject(resource, image, resource.SpriteId, resource.Depth, originX, originY)); } // Get the object nodes App.Room.Nodes = form.GMProject.ProjectTree.Nodes[objectIndex].Nodes; // Set textures for loaded objects SetTextures(); return(true); } }
/// <summary> /// Reads a Game Maker project file /// </summary> public void ReadProject(string file) { // If the file does not exist, throw exception if (File.Exists(file) == false) { throw new Exception("The Game Maker project file does not exist."); } // Get file extension string ext = file.Substring(file.LastIndexOf('.')).ToLower(); // If a GMS project file if (ext == ".gmx") { // Read in the project as a Game Maker Studio project and return ReadProjectGMS(file); return; } // Get file size FileInfo info = new FileInfo(file); long length = info.Length; // Create a new GM file reader using (GMFileReader reader = new GMFileReader(new FileStream(file, FileMode.Open, FileAccess.Read))) { // Progress event ProgressChanged("Starting project read...", reader.BaseStream.Position, length); // Read the magic number int id = reader.ReadGMInt(); // If the magic number was incorrect, not a Game Maker project file if (id != 1234321) { throw new Exception("Not a valid Game Maker project file."); } // Get Game Maker project file version int version = reader.ReadGMInt(); // Check version switch (version) { case 500: this.GameMakerVersion = GMVersionType.GameMaker50; break; case 510: this.GameMakerVersion = GMVersionType.GameMaker51; break; case 520: this.GameMakerVersion = GMVersionType.GameMaker52; break; case 530: this.GameMakerVersion = GMVersionType.GameMaker53; break; case 600: this.GameMakerVersion = GMVersionType.GameMaker60; break; case 701: this.GameMakerVersion = GMVersionType.GameMaker70; break; case 800: this.GameMakerVersion = GMVersionType.GameMaker80; break; case 810: this.GameMakerVersion = GMVersionType.GameMaker81; break; } // Skip over reserved bytes if (version < 600) { reader.ReadGMBytes(4); } // Game Maker 7 project file encryption if (version == 701) { // Bill and Fred, psssttt they like each other ;) int bill = reader.ReadGMInt(); int fred = reader.ReadGMInt(); // Skip bytes to treasure. reader.ReadGMBytes(bill * 4); // Get the seed for swap table int seed = reader.ReadGMInt(); // Skip bytes to get out of the junk yard reader.ReadGMBytes(fred * 4); // Read first byte of Game id (Not encrypted) byte b = reader.ReadByte(); // Set the seed reader.SetSeed(seed); // Read game id id = reader.ReadGMInt(b); } else // Read game id normally { id = reader.ReadGMInt(); } // Skip unknown bytes reader.ReadGMBytes(16); // Read settings ProgressChanged("Reading Settings...", reader.BaseStream.Position, length); // Read main project objects this.Settings = GMSettings.ReadSettings(reader); this.Settings.GameIdentifier = id; // If the version is greater than Game Maker 7.0 if (version > 701) { // Read triggers and constants. this.Triggers = GMTrigger.ReadTriggers(reader); this.Settings.Constants = GMConstant.ReadConstants(reader); } // Read sounds ProgressChanged("Reading Sounds...", reader.BaseStream.Position, length); this.Sounds = GMSound.ReadSounds(reader); // Read sprites ProgressChanged("Reading Sprites...", reader.BaseStream.Position, length); this.Sprites = GMSprite.ReadSprites(reader); // Read backgrounds ProgressChanged("Reading Backgrounds...", reader.BaseStream.Position, length); this.Backgrounds = GMBackground.ReadBackgrounds(reader); // Read paths ProgressChanged("Reading Paths...", reader.BaseStream.Position, length); this.Paths = GMPath.ReadPaths(reader); // Read scripts ProgressChanged("Reading Scripts...", reader.BaseStream.Position, length); this.Scripts = GMScript.ReadScripts(reader); // Get version int version2 = reader.ReadGMInt(); // Check version if (version2 != 440 && version2 != 540 && version2 != 800) { throw new Exception("Unsupported Pre-Font/Pre-Data File object version."); } // If version is old, read data files else, read fonts. if (version2 == 440) { // Read data files ProgressChanged("Reading Data Files...", reader.BaseStream.Position, length); this.DataFiles = GMDataFile.ReadDataFiles(reader); } else { // Read fonts ProgressChanged("Reading Fonts...", reader.BaseStream.Position, length); this.Fonts = GMFont.ReadFonts(version2, reader); } // Read timelines ProgressChanged("Reading Timelines...", reader.BaseStream.Position, length); this.Timelines = GMTimeline.ReadTimelines(reader); // Read objects ProgressChanged("Reading Objects...", reader.BaseStream.Position, length); this.Objects = GMObject.ReadObjects(reader); // Read rooms ProgressChanged("Reading Rooms...", reader.BaseStream.Position, length); this.Rooms = GMRoom.ReadRooms(this.Objects, reader); // Read last ids for instances and tiles this.LastInstanceId = reader.ReadGMInt(); this.LastTileId = reader.ReadGMInt(); // If the version is above 6.1, read include files and packages if (version >= 700) { // Read includes ProgressChanged("Reading Includes...", reader.BaseStream.Position, length); this.Settings.Includes = GMInclude.ReadIncludes(reader); // Read packages ProgressChanged("Reading Packages...", reader.BaseStream.Position, length); this.Packages.AddRange(GMPackage.ReadPackages(reader)); } // Read game information ProgressChanged("Reading Game Information...", reader.BaseStream.Position, length); this.GameInformation = GMGameInformation.ReadGameInformation(reader); // Get version version = reader.ReadGMInt(); // Check version if (version != 500) { throw new Exception("Unsupported Post-Game Information object version."); } // Read libraries ProgressChanged("Reading Libraries...", reader.BaseStream.Position, length); this.Libraries = GMLibrary.ReadLibraries(reader); // Read project tree ProgressChanged("Reading Project Tree...", reader.BaseStream.Position, length); this.ProjectTree = GMNode.ReadTree(file.Substring(file.LastIndexOf(@"\") + 1), reader); // Progress event ProgressChanged("Finished Reading Project.", reader.BaseStream.Position, length); } }
/// <summary> /// Reads a Game Maker Studio project file /// </summary> private void ReadProjectGMS(string file) { // Set version GameMakerVersion = GMVersionType.GameMakerStudio; // Path with project file removed string folder = file.Remove(file.LastIndexOf("\\")); // Set up resource directory strings Dictionary <GMResourceType, string> directories = new Dictionary <GMResourceType, string>(); directories.Add(GMResourceType.Assets, file); directories.Add(GMResourceType.DataFiles, file); directories.Add(GMResourceType.Configs, file); directories.Add(GMResourceType.Constants, file); directories.Add(GMResourceType.Hash, file); directories.Add(GMResourceType.Backgrounds, folder + "\\" + "background"); directories.Add(GMResourceType.Objects, folder + "\\" + "objects"); directories.Add(GMResourceType.Rooms, folder + "\\" + "rooms"); directories.Add(GMResourceType.Sprites, folder + "\\" + "sprites"); directories.Add(GMResourceType.Sounds, folder + "\\" + "sound"); directories.Add(GMResourceType.TimeLines, folder + "\\" + "timelines"); directories.Add(GMResourceType.Shaders, folder + "\\" + "shaders"); directories.Add(GMResourceType.Scripts, folder + "\\" + "scripts"); directories.Add(GMResourceType.Paths, folder + "\\" + "paths"); // Resource load index int index = 0; // Iterate through directories foreach (KeyValuePair <GMResourceType, string> item in directories) { // Increment directory index index++; // If the directory does not exist, continue if (Path.GetExtension(item.Value) != ".gmx" && !Directory.Exists(item.Value)) { continue; } // Progress changed ProgressChanged("Reading " + item.Key.ToString() + "...", index, directories.Count); // Load data based on resource type switch (item.Key) { case GMResourceType.Hash: Settings.Hash = ReadHashGMX(item.Value); break; case GMResourceType.Assets: ProjectTree = GMNode.ReadTreeGMX(item.Value); Assets = (List <string>)ProjectTree.Tag; break; case GMResourceType.DataFiles: DataFiles = GMDataFile.ReadDataFilesGMX(item.Value, out LastDataFileId); break; case GMResourceType.Sprites: Sprites = GMSprite.ReadSpritesGMX(item.Value, ref Assets); break; //case GMResourceType.Configs: Settings.Configs = GMSettings.GetConfigsGMX(item.Value); break; //case GMResourceType.Constants: Settings.Constants = GMSettings.ReadConstantsGMX(item.Value); break; case GMResourceType.Backgrounds: Backgrounds = GMBackground.ReadBackgroundsGMX(item.Value, ref Assets); break; case GMResourceType.Objects: Objects = GMObject.ReadObjectsGMX(item.Value, ref Assets); break; case GMResourceType.Rooms: Rooms = GMRoom.ReadRoomsGMX(item.Value, ref Assets, out LastTileId); break; //case GMResourceType.TimeLines: Timelines = GMTimeline.ReadTimelinesGMX(item.Value, Assets); break; //case GMResourceType.Sounds: Sounds = GMSound.ReadSoundsGMX(item.Value, ref Assets); break; //case GMResourceType.Shaders: Shaders = GMShader.ReadShadersGMX(item.Value, ref Assets); break; //case GMResourceType.Scripts: Scripts = GMScript.ReadScriptsGMX(item.Value, ref Assets); break; //case GMResourceType.Paths: Paths = GMPath.ReadPathsGMX(item.Value, ref Assets); break; //case GMResourceType.TimeLines: Timelines = GMTimeline.ReadTimelinesGMX(item.Value, Assets); break; } } // Retrieve tutorial data foreach (GMNode node in ProjectTree.Nodes) { // If the node is the tutorial state node and it has the nodes we're looking for if (node.ResourceType == GMResourceType.TutorialState && node.Nodes != null && node.Nodes.Length == 3) { Settings.IsTutorial = node.Nodes[0].Nodes == null ? Settings.IsTutorial : GMResource.GMXBool(node.Nodes[0].Nodes[0].Name, true); Settings.TutorialName = node.Nodes[1].Nodes == null ? Settings.TutorialName : GMResource.GMXString(node.Nodes[1].Nodes[0].FilePath, ""); Settings.TutorialPage = node.Nodes[2].Nodes == null ? Settings.TutorialPage : GMResource.GMXInt(node.Nodes[2].Nodes[0].Name, 0); } } // Progress event ProgressChanged("Finished Reading Project.", index, directories.Count); }
/// <summary> /// Reads sprites from Game Maker project file. /// </summary> private GMList<GMSprite> ReadSprites() { // Get version. int version = ReadInt(); // Check version. if (version != 400 && version != 800) throw new Exception("Unsupported Pre-Sprite object version."); // Create a new list of sprites. GMList<GMSprite> sprites = new GMList<GMSprite>(); // Amount of sprite ids. int num = ReadInt(); // Iterate through sprites. for (int i = 0; i < num; i++) { // If version is 8.0, start inflate. if (version == 800) Decompress(); // If the sprite at index does not exists, continue. if (ReadBool() == false) { sprites.LastId++; EndDecompress(); continue; } // Create a new sprite object. GMSprite sprite = new GMSprite(); // Set sprite id. sprite.Id = i; // Read sprite data. sprite.Name = ReadString(); // If version is 8.0, get last changed. if (version == 800) sprite.LastChanged = ReadDouble(); // Get version. version = ReadInt(); // Check version. if (version != 400 && version != 542 && version != 800) throw new Exception("Unsupported Sprite object version."); // If less than version 8.0. if (version < 800) { // Read sprite data sprite.Width = ReadInt(); sprite.Height = ReadInt(); sprite.BoundingBoxLeft = ReadInt(); sprite.BoundingBoxRight = ReadInt(); sprite.BoundingBoxBottom = ReadInt(); sprite.BoundingBoxTop = ReadInt(); sprite.Transparent = ReadBool(); // Check version. if (version > 400) { // Read sprite data sprite.SmoothEdges = ReadBool(); sprite.Preload = ReadBool(); } // Read sprite data. sprite.BoundingBoxMode = (BoundingBoxType)ReadInt(); sprite.Precise = ReadBool(); // Check version. if (version == 400) { // Read sprtie data. sprite.UseVideoMemory = ReadBool(); sprite.LoadOnlyOnUse = ReadBool(); } // Read sprite data. sprite.OriginX = ReadInt(); sprite.OriginY = ReadInt(); // Sprite number of sub images sprite.SubImages = new GMImage[ReadInt()]; // Iterate through sub-images for (int j = 0; j < sprite.SubImages.Length; j++) { // If the sub-image at index does not exists, continue. if (ReadInt() == -1) continue; // Create a new image object. GMImage image = new GMImage(); // Get size of image data. int size = ReadInt(); // Get image data. image.Data = ReadBytes(size); // Insert compressed image data. sprite.SubImages[j] = image; } } else { // Read sprite data. sprite.OriginX = ReadInt(); sprite.OriginY = ReadInt(); // Sprite number of sub images. sprite.SubImages = new GMImage[ReadInt()]; // Iterate through sub-images. for (int j = 0; j < sprite.SubImages.Length; j++) { // Get version. version = ReadInt(); // Check version. if (version != 800) throw new Exception("Unsupported Sprite object version."); // Get width and height of image. int width = ReadInt(); int height = ReadInt(); // If the sprite size is not zero. if (width != 0 && height != 0) { // Create a new image object. GMImage image = new GMImage(); image.Compressed = false; // Set image data. image.Width = width; image.Height = height; // Get size of image data. int size = ReadInt(); // Get image data. image.Data = ReadBytes(size); // Insert compressed image data. sprite.SubImages[j] = image; } } // Read sprite data. sprite.ShapeMode = (ShapeType)ReadInt(); sprite.AlphaTolerance = ReadInt(); sprite.UseSeperateCollisionMasks = ReadBool(); sprite.BoundingBoxMode = (BoundingBoxType)ReadInt(); sprite.BoundingBoxLeft = ReadInt(); sprite.BoundingBoxRight = ReadInt(); sprite.BoundingBoxBottom = ReadInt(); sprite.BoundingBoxTop = ReadInt(); } // End object inflate. EndDecompress(); // Add sprite. sprites.Add(sprite); } // Return sprites. return sprites; }
public static int[] Generate(Image frame, GMSprite sprite) { int[] maskArray = new int[frame.Width * frame.Height]; int left = sprite.BBox.X; int right = sprite.BBox.Y; int bottom = sprite.BBox.Width; int top = sprite.BBox.Height; int mLeft = Math.Max(0, Math.Min(frame.Height - 1, top)); int mBottom = Math.Max(0, Math.Min(frame.Height - 1, bottom)); int mTop = Math.Max(0, Math.Min(frame.Width - 1, left)); int mRight = Math.Max(0, Math.Min(frame.Width - 1, right)); if (mLeft != mBottom && mTop != mRight) { switch (sprite.MaskMode) { case GMSprite.SpriteMaskMode.RECTANGLE: { for (int y = mLeft; y <= mBottom; y++) { for (int x = mTop; x <= mRight; x++) { //maskArray[y * frame.Width + x] |= 1 << 7 - (x & 7); maskArray[y * frame.Width + x]++; } } break; } case GMSprite.SpriteMaskMode.DISK: { float fXYHalf = (left + right) / 2; float fWHHalf = (bottom + top) / 2; float fXYX = fXYHalf - left + 0.5f; float fWHH = fWHHalf - top + 0.5f; for (int y = mLeft; y <= mBottom; y++) { for (int x = mTop; x <= mRight; x++) { if (fXYX > 0f && fWHH > 0f && Math.Pow((x - fXYHalf) / fXYX, 2.0) + Math.Pow((y - fWHHalf) / fWHH, 2.0) <= 1.0) { //maskArray[y * frame.Width + x] |= 1 << 7 - (x & 7); maskArray[y * frame.Width + x]++; } } } break; } case GMSprite.SpriteMaskMode.DIAMOND: { float fXYHalf = (left + right) / 2; float fWHHalf = (bottom + top) / 2; float fXYX = fXYHalf - left + 0.5f; float fWHH = fWHHalf - top + 0.5f; for (int y = mLeft; y <= mBottom; y++) { for (int x = mTop; x <= mRight; x++) { if (fXYX > 0f && fWHH > 0f && Math.Abs((x - fXYHalf) / fXYX) + Math.Abs((y - fWHHalf) / fWHH) <= 1f) { //maskArray[y * frame.Width + x] |= 1 << 7 - (x & 7); maskArray[y * frame.Width + x]++; } } } break; } case GMSprite.SpriteMaskMode.PRECISE: { for (int y = mLeft; y <= mBottom; y++) { for (int x = mTop; x <= mRight; x++) { if (((Bitmap)frame).GetPixel(x, y).A > sprite.AlphaTolerance) { //maskArray[y * frame.Width + x] |= 1 << 7 - (x & 7); maskArray[y * frame.Width + x]++; } } } break; } } } //DumpIntArray(maskArray); return(maskArray); }
public static AssetSprite.CollisionMaskInfo GetInfoForSprite(ProjectFile pf, GMSprite spr, out List <Bitmap> bitmaps, bool suggestPrecise = false) { bitmaps = new List <Bitmap>(spr.TextureItems.Count); var info = new AssetSprite.CollisionMaskInfo { Mode = (MaskMode)spr.BBoxMode }; if (spr.SepMasks == GMSprite.SepMaskType.AxisAlignedRect) { info.Type = MaskType.Rectangle; } else if (spr.SepMasks == GMSprite.SepMaskType.RotatedRect) { info.Type = MaskType.RectangleWithRotation; } // Some basic conditions to bail if (spr.CollisionMasks.Count != 1 && spr.CollisionMasks.Count != spr.TextureItems.Count) { return(info); } if (spr.CollisionMasks.Count == 0) { return(info); } // Get bitmaps from frames bitmaps = GetBitmaps(pf, spr.Width, spr.Height, spr.TextureItems); List <BitmapData> bitmapData = new List <BitmapData>(bitmaps.Count); foreach (var item in bitmaps) { bitmapData.Add(item.BasicLockBits()); } int boundLeft = Math.Clamp(spr.MarginLeft, 0, spr.Width), boundRight = Math.Clamp(spr.MarginRight, 0, spr.Width - 1), boundTop = Math.Clamp(spr.MarginTop, 0, spr.Height), boundBottom = Math.Clamp(spr.MarginBottom, 0, spr.Height - 1); switch (spr.SepMasks) { case GMSprite.SepMaskType.AxisAlignedRect: case GMSprite.SepMaskType.RotatedRect: switch (info.Mode) { case MaskMode.Automatic: // Scan for the lowest alpha value in the bounding box // When comparing each pixel, compare to the one in that spot with the highest alpha in every frame bool foundNonzero = false; byte lowest = 0; byte highest = 0; int stride = ((spr.Width + 7) / 8) * 8; FastBitArray mask = new FastBitArray(spr.CollisionMasks[0].Memory.Span); int strideFactor = boundTop * stride; for (int y = boundTop; y <= boundBottom; y++) { for (int x = boundLeft; x <= boundRight; x++) { if (mask.GetReverse(x + strideFactor)) { byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y); if (highestAlpha > highest) { highest = highestAlpha; } if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest)) { lowest = highestAlpha; foundNonzero = true; } } } strideFactor += stride; } if (foundNonzero) { if (lowest == highest) { lowest = 0; // Could be anything } else { --lowest; } } info.AlphaTolerance = lowest; break; case MaskMode.Manual: info.Left = spr.MarginLeft; info.Right = spr.MarginRight; info.Top = spr.MarginTop; info.Bottom = spr.MarginBottom; break; } break; case GMSprite.SepMaskType.Precise: { int stride = ((spr.Width + 7) / 8) * 8; bool foundNonzero = false; byte lowest = 0; byte highest = 0; if (spr.CollisionMasks.Count > 1 && spr.CollisionMasks.Count == spr.TextureItems.Count) { info.Type = MaskType.PrecisePerFrame; unsafe { for (int i = 0; i < spr.CollisionMasks.Count; i++) { BitmapData item = bitmapData[i]; FastBitArray mask = new FastBitArray(spr.CollisionMasks[i].Memory.Span); int strideFactor = boundTop * stride; for (int y = boundTop; y <= boundBottom; y++) { for (int x = boundLeft; x <= boundRight; x++) { if (mask.GetReverse(x + strideFactor)) { byte val = *((byte *)item.Scan0 + (x * 4) + (y * item.Stride) + 3); if (val > highest) { highest = val; } if (val != 0 && (!foundNonzero || val < lowest)) { lowest = val; foundNonzero = true; } } } strideFactor += stride; } } } } else { info.Type = MaskType.Precise; // Scan for highest alpha, as well as diamond/ellipses FastBitArray mask = new FastBitArray(spr.CollisionMasks[0].Memory.Span); bool isDiamond = true, isEllipse = true; float centerX = ((spr.MarginLeft + spr.MarginRight) / 2); float centerY = ((spr.MarginTop + spr.MarginBottom) / 2); float radiusX = centerX - spr.MarginLeft + 0.5f; float radiusY = centerY - spr.MarginTop + 0.5f; int strideFactor = boundTop * stride; if (!suggestPrecise && radiusX > 0f && radiusY > 0f) { for (int y = boundTop; y <= boundBottom; y++) { for (int x = boundLeft; x <= boundRight; x++) { float normalX = (x - centerX) / radiusX; float normalY = (y - centerY) / radiusY; bool inDiamond = Math.Abs(normalX) + Math.Abs(normalY) <= 1f; bool inEllipse = Math.Pow(normalX, 2.0d) + Math.Pow(normalY, 2.0d) <= 1.0d; if (mask.GetReverse(x + strideFactor)) { isDiamond &= inDiamond; isEllipse &= inEllipse; byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y); if (highestAlpha > highest) { highest = highestAlpha; } if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest)) { lowest = highestAlpha; foundNonzero = true; } } // Can't eliminate based on this, they can be split into pieces with multiple frames //else //{ // isDiamond &= !inDiamond; // isEllipse &= !inEllipse; //} } strideFactor += stride; } } else { // Version without diamond/ellipse checks isDiamond = false; isEllipse = false; for (int y = boundTop; y <= boundBottom; y++) { for (int x = boundLeft; x <= boundRight; x++) { if (mask.GetReverse(x + strideFactor)) { byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y); if (highestAlpha > highest) { highest = highestAlpha; } if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest)) { lowest = highestAlpha; foundNonzero = true; } } } strideFactor += stride; } } if (isDiamond) { info.Type = MaskType.Diamond; } else if (isEllipse) { info.Type = MaskType.Ellipse; } } if (info.Mode == MaskMode.Manual || (info.Mode == MaskMode.Automatic && info.Type != MaskType.Precise && info.Type != MaskType.PrecisePerFrame)) { info.Left = spr.MarginLeft; info.Right = spr.MarginRight; info.Top = spr.MarginTop; info.Bottom = spr.MarginBottom; } if (info.Mode == MaskMode.Automatic || info.Type == MaskType.Precise || (info.Mode == MaskMode.Manual && info.Type == MaskType.PrecisePerFrame)) { if (foundNonzero) { if (lowest == highest) { lowest = 0; // Could be anything } else { --lowest; } } info.AlphaTolerance = lowest; } } break; } for (int i = 0; i < bitmaps.Count; i++) { bitmaps[i].UnlockBits(bitmapData[i]); } return(info); }
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; }
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 } },
/// <summary> /// Gets a .net tree node from a GM node. Stores original GM node data in tag property. /// </summary> /// <param name="node">The GM node to convert to treenode.</param> /// <returns>A tree node version of the GM node.</returns> public static TreeNode GetTreeNodeFromGMNode(GMProject project, GMNode node, List <Image> images) { // Set-up tree node. TreeNode treeNode = new TreeNode(); treeNode.Text = node.Name; treeNode.Tag = node; // If the list does not have the folder graphics, add them. if (images != null && images.Count < 2) { images.Add((Image)GMare.Properties.Resources.file_close.Clone()); images.Add((Image)GMare.Properties.Resources.file_open.Clone()); images.Add((Image)GMare.Properties.Resources.instance.Clone()); } switch (node.NodeType) { // If a child node, get appropriate resource key. case GMNodeType.Child: // Do action based on object type. switch (node.ResourceType) { // Sprites. case GMResourceType.Sprites: // If no images to load, break. if (images == null) { break; } // Get sprite. GMSprite sprite = project.Sprites.Find(delegate(GMSprite s) { return(s.Id == node.Id); }); // If a sprite was found. if (sprite != null) { // Get image. Bitmap image = GMUtilities.GetBitmap(sprite.SubImages[0]); // Set transparency if needed. if (sprite.Transparent && sprite.SubImages[0].Compressed) { image.MakeTransparent(image.GetPixel(0, image.Height - 1)); } images.Add(image); treeNode.ImageIndex = images.Count - 1; treeNode.SelectedImageIndex = images.Count - 1; } else { treeNode.ImageIndex = 2; treeNode.SelectedImageIndex = 2; } break; // Objects. case GMResourceType.Objects: // If no images to load, break. if (images == null) { break; } // Get the object id, use it to get the sprite id. GMObject obj = project.Objects.Find(delegate(GMObject o) { return(o.Id == node.Id); }); GMSprite spt = project.Sprites.Find(delegate(GMSprite s) { return(s.Id == obj.SpriteId); }); // If a sprite was found. if (spt != null && spt.SubImages != null && spt.SubImages.Length > 0) { // Get image. Bitmap image = GMUtilities.GetBitmap(spt.SubImages[0]); // Set transparency if needed. if (spt.Transparent && spt.SubImages[0].Compressed) { image.MakeTransparent(image.GetPixel(0, image.Height - 1)); } images.Add(image); treeNode.ImageIndex = images.Count - 1; treeNode.SelectedImageIndex = images.Count - 1; } else { treeNode.ImageIndex = 2; treeNode.SelectedImageIndex = 2; } break; // Rooms. case GMResourceType.Rooms: treeNode.ImageKey = ImageKeyRoom; treeNode.SelectedImageKey = ImageKeyRoomSelected; break; } break; // Folders or parents. default: treeNode.ImageIndex = 0; treeNode.SelectedImageIndex = 1; treeNode.ImageKey = ImageKeyGroup; treeNode.SelectedImageKey = ImageKeyGroupSelected; break; } // If no child nodes, return the top node. if (node.Nodes == null) { return(treeNode); } // Iterate through nodes. for (int i = 0; i < node.Children; i++) { // Set the node. treeNode.Nodes.Add(GetTreeNodeFromGMNode(project, node.Nodes[i], images)); } // Return a .net tree node. return(treeNode); }