static void Main(string[] args) { byte[] art; ColorPalette palette; List <MappingsFrame> map; List <DPLCFrame> dplc; SpriteInfo spriteInfo; LevelData.littleendian = false; LongOpt[] opts = new[] { new LongOpt("help", Argument.No, null, 'h'), new LongOpt("padding", Argument.Required, null, 'p'), new LongOpt("columns", Argument.Required, null, 'c'), new LongOpt("width", Argument.Required, null, 'w'), new LongOpt("background", Argument.Required, null, 'b'), new LongOpt("grid", Argument.No, null, 'g') }; Getopt getopt = new Getopt("SpriteSheetGen", args, Getopt.digest(opts), opts); int padding = 2; int columns = 8; int width = 0; bool fixedwidth = false; int gridsize = -1; Color background = Color.Transparent; int opt = getopt.getopt(); while (opt != -1) { switch (opt) { case 'h': Console.Write(Properties.Resources.HelpText); return; case 'p': padding = int.Parse(getopt.Optarg); break; case 'c': columns = int.Parse(getopt.Optarg); break; case 'w': width = int.Parse(getopt.Optarg); columns = -1; fixedwidth = true; break; case 'g': gridsize = 0; break; case 'b': if (getopt.Optarg.StartsWith("#")) { background = Color.FromArgb((int)(0xFF000000 | uint.Parse(getopt.Optarg.Substring(1), System.Globalization.NumberStyles.HexNumber))); } else { background = Color.FromName(getopt.Optarg.Replace(" ", "")); } break; } opt = getopt.getopt(); } string filename; Console.Write("File: "); if (getopt.Optind == args.Length) { filename = Console.ReadLine(); } else { filename = args[getopt.Optind]; Console.WriteLine(filename); } filename = Path.GetFullPath(filename); Environment.CurrentDirectory = Path.GetDirectoryName(filename); spriteInfo = IniSerializer.Deserialize <SpriteInfo>(filename); if (spriteInfo.MappingsGame == EngineVersion.Invalid) { spriteInfo.MappingsGame = spriteInfo.Game; } if (spriteInfo.DPLCGame == EngineVersion.Invalid) { spriteInfo.DPLCGame = spriteInfo.Game; } MultiFileIndexer <byte> tiles = new MultiFileIndexer <byte>(); foreach (SonicRetro.SonLVL.API.FileInfo file in spriteInfo.Art) { tiles.AddFile(new List <byte>(Compression.Decompress(file.Filename, spriteInfo.ArtCompression)), file.Offset); } art = tiles.ToArray(); tiles.Clear(); byte[] tmp = null; using (Bitmap palbmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed)) palette = palbmp.Palette; for (int i = 0; i < 256; i++) { palette.Entries[i] = Color.Black; } foreach (PaletteInfo palent in spriteInfo.Palette) { tmp = File.ReadAllBytes(palent.Filename); SonLVLColor[] palfile; palfile = new SonLVLColor[tmp.Length / 2]; for (int pi = 0; pi < tmp.Length; pi += 2) { palfile[pi / 2] = new SonLVLColor(SonicRetro.SonLVL.API.ByteConverter.ToUInt16(tmp, pi)); } for (int pa = 0; pa < palent.Length; pa++) { palette.Entries[pa + palent.Destination] = palfile[pa + palent.Source].RGBColor; } } palette.Entries[0] = background; Dictionary <string, int> labels = new Dictionary <string, int>(); tmp = null; switch (spriteInfo.MappingsFormat) { case MappingsFormat.Binary: tmp = File.ReadAllBytes(spriteInfo.MappingsFile); break; case MappingsFormat.ASM: case MappingsFormat.Macro: tmp = LevelData.ASMToBin(spriteInfo.MappingsFile, spriteInfo.MappingsGame, out labels); break; } map = MappingsFrame.Load(tmp, spriteInfo.MappingsGame, labels); if (!string.IsNullOrEmpty(spriteInfo.DPLCFile)) { labels = new Dictionary <string, int>(); tmp = null; switch (spriteInfo.DPLCFormat) { case MappingsFormat.Binary: tmp = File.ReadAllBytes(spriteInfo.DPLCFile); break; case MappingsFormat.ASM: case MappingsFormat.Macro: tmp = LevelData.ASMToBin(spriteInfo.DPLCFile, spriteInfo.DPLCGame, out labels); break; } dplc = DPLCFrame.Load(tmp, spriteInfo.DPLCGame, labels); } else { dplc = null; } List <BitmapBits> spritesLow = new List <BitmapBits>(map.Count); List <BitmapBits> spritesHigh = new List <BitmapBits>(map.Count); List <BitmapBits> spritesMerged = new List <BitmapBits>(map.Count); List <Point> offsets = new List <Point>(map.Count); List <Point> centers = new List <Point>(map.Count); for (int i = 0; i < map.Count; i++) { if (map[i].TileCount == 0) { //File.AppendAllText("log.txt", "Frame " + i + " empty.\r\n"); continue; } Sprite spr; if (dplc != null) { spr = LevelData.MapFrameToBmp(art, map[i], dplc[i], spriteInfo.StartPalette); } else { spr = LevelData.MapFrameToBmp(art, map[i], spriteInfo.StartPalette); } BitmapBits sprLow = spr.GetBitmapLow(); BitmapBits sprHigh = spr.GetBitmapHigh(); BitmapBits sprMerged = spr.GetBitmap(); int cx = -spr.X; int cy = -spr.Y; for (int _x = 0; _x < sprMerged.Width; _x++) { for (int _y = 0; _y < sprMerged.Height; _y++) { if (sprMerged[_x, _y] != 0) { sprMerged = sprMerged.GetSection(_x, 0, sprMerged.Width - _x, sprMerged.Height); sprLow = sprLow.GetSection(_x, 0, sprLow.Width - _x, sprLow.Height); sprHigh = sprHigh.GetSection(_x, 0, sprHigh.Width - _x, sprHigh.Height); cx -= _x; goto checkright; } } } checkright: for (int _x = sprMerged.Width - 1; _x >= 0; _x--) { for (int _y = 0; _y < sprMerged.Height; _y++) { if (sprMerged[_x, _y] != 0) { sprMerged = sprMerged.GetSection(0, 0, _x + 1, sprMerged.Height); sprLow = sprLow.GetSection(0, 0, _x + 1, sprLow.Height); sprHigh = sprHigh.GetSection(0, 0, _x + 1, sprHigh.Height); goto checktop; } } } checktop: for (int _y = 0; _y < sprMerged.Height; _y++) { for (int _x = 0; _x < sprMerged.Width; _x++) { if (sprMerged[_x, _y] != 0) { sprMerged = sprMerged.GetSection(0, _y, sprMerged.Width, sprMerged.Height - _y); sprLow = sprLow.GetSection(0, _y, sprLow.Width, sprLow.Height - _y); sprHigh = sprHigh.GetSection(0, _y, sprHigh.Width, sprHigh.Height - _y); cy -= _y; goto checkbottom; } } } checkbottom: for (int _y = sprMerged.Height - 1; _y >= 0; _y--) { for (int _x = 0; _x < sprMerged.Width; _x++) { if (sprMerged[_x, _y] != 0) { sprMerged = sprMerged.GetSection(0, 0, sprMerged.Width, _y + 1); sprLow = sprLow.GetSection(0, 0, sprLow.Width, _y + 1); sprHigh = sprHigh.GetSection(0, 0, sprHigh.Width, _y + 1); goto checkdone; } } } checkdone: spritesMerged.Add(sprMerged); spritesLow.Add(sprLow); spritesHigh.Add(sprHigh); centers.Add(new Point(cx, cy)); } if (gridsize == 0) { for (int i = 0; i < spritesMerged.Count; i++) { gridsize = Math.Max(gridsize, Math.Max(spritesMerged[i].Width, spritesMerged[i].Height)); } if (!fixedwidth) { width = (padding * 2 + gridsize) * columns; } } int x = padding; int y = padding; int height = 0; int rowcnt = 0; int rowheight = 0; for (int i = 0; i < spritesMerged.Count; i++) { BitmapBits spr = spritesMerged[i]; Point off; if (gridsize == -1) { if (fixedwidth && x + spr.Width + padding > width) { x = padding; y += rowheight; rowheight = 0; } off = new System.Drawing.Point(x, y); centers[i] = new Point(centers[i].X + off.X, centers[i].Y + off.Y); if (!fixedwidth) { width = Math.Max(width, x + spr.Width + padding); } height = Math.Max(height, y + spr.Height + padding); if (spr.Height + 2 * padding > rowheight) { rowheight = spr.Height + 2 * padding; } if (!fixedwidth && ++rowcnt == columns) { x = padding; y += rowheight; rowcnt = 0; rowheight = 0; } else { x += spr.Width + 2 * padding; } } else { if (fixedwidth && x + gridsize + padding > width) { x = padding; y += gridsize + 2 * padding; } off = new Point(x + (gridsize - spr.Width) / 2, y + (gridsize - spr.Height) / 2); centers[i] = new Point(centers[i].X + off.X, centers[i].Y + off.Y); height = Math.Max(height, y + gridsize + padding); if (!fixedwidth && ++rowcnt == columns) { x = padding; y += gridsize + 2 * padding; rowcnt = 0; } else { x += gridsize + 2 * padding; } } offsets.Add(off); } BitmapBits bmpMerged = new BitmapBits(width, height); for (int i = 0; i < spritesMerged.Count; i++) { bmpMerged.DrawBitmap(spritesMerged[i], offsets[i]); } BitmapBits bmpLow = new BitmapBits(width, height); for (int i = 0; i < spritesLow.Count; i++) { bmpLow.DrawBitmap(spritesLow[i], offsets[i]); } BitmapBits bmpHigh = new BitmapBits(width, height); for (int i = 0; i < spritesHigh.Count; i++) { bmpHigh.DrawBitmap(spritesHigh[i], offsets[i]); } BitmapBits bmpCenter = new BitmapBits(width, height); for (int i = 0; i < centers.Count; i++) { bmpCenter[centers[i].X, centers[i].Y] = 1; } Console.Write("Save as: "); if (getopt.Optind + 1 >= args.Length) { filename = Console.ReadLine(); } else { filename = args[getopt.Optind + 1]; Console.WriteLine(filename); } filename = Path.GetFullPath(filename); bmpMerged.ToBitmap(palette).Save(filename); if (!bmpLow.Bits.FastArrayEqual(0) && !bmpHigh.Bits.FastArrayEqual(0)) { bmpLow.ToBitmap(palette).Save(Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename) + "_low" + Path.GetExtension(filename))); bmpHigh.ToBitmap(palette).Save(Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename) + "_high" + Path.GetExtension(filename))); } bmpCenter.ToBitmap1bpp(Color.Black, Color.White).Save(Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename) + "_center" + Path.GetExtension(filename))); }
private static void SpriteToMap(Rectangle sprite, Point center, BitmapBits plane, List <byte[]> tiles, MappingsFrame mapframe, DPLCFrame dplcframe, byte startpal, bool pri, bool twoplayer) { BitmapBits bmp = plane.GetSection(sprite); if (bmp.Width % 8 != 0 || bmp.Height % 8 != 0) { int w = bmp.Width; if (w % 8 != 0) { w = (w & ~7) + 8; } int h = bmp.Height; if (h % 8 != 0) { h = (h & ~7) + 8; } BitmapBits tmp = new BitmapBits(w, h); tmp.DrawBitmapBounded(bmp, 0, 0); bmp = tmp; } int tw = bmp.Width / 8; int th = bmp.Height / 8; byte[,] tilepals = new byte[tw, th]; for (int ty = 0; ty < th; ty++) { for (int tx = 0; tx < tw; tx++) { tilepals[tx, ty] = 0xFF; int[] palcnt = new int[4]; for (int y = 0; y < 8; y++) { for (int x = 0; x < 8; x++) { if (bmp[tx * 8 + x, ty * 8 + y] % 16 != 0) { palcnt[bmp[tx * 8 + x, ty * 8 + y] / 16]++; } } } if (!palcnt.FastArrayEqual(0)) { tilepals[tx, ty] = 0; for (int i = 1; i < 4; i++) { if (palcnt[i] > palcnt[tilepals[tx, ty]]) { tilepals[tx, ty] = (byte)i; } } } } } int numtiles = 0; for (int ty = 0; ty < th; ty++) { for (int tx = 0; tx < tw; tx++) { if (tilepals[tx, ty] != 0xFF) { int w = Math.Min(4, (tw) - tx); for (int x = 1; x < w; x++) { if (tilepals[tx + x, ty] != tilepals[tx, ty]) { w = x; } } int h = Math.Min(4, (th) - ty); for (int y = 1; y < h; y++) { for (int x = 0; x < w; x++) { if (tilepals[tx + x, ty + y] != tilepals[tx, ty]) { h = y; } } } if (twoplayer && h % 2 == 1) { h++; } if (dplcframe != null) { mapframe.Tiles.Add(new MappingsTile((short)((tx * 8) - center.X), (short)((ty * 8) - center.Y), (byte)w, (byte)h, (ushort)numtiles, false, false, startpal, pri)); dplcframe.Tiles.Add(new DPLCEntry((byte)(w * h), (ushort)tiles.Count)); } else { mapframe.Tiles.Add(new MappingsTile((short)((tx * 8) - center.X), (short)((ty * 8) - center.Y), (byte)w, (byte)h, (ushort)tiles.Count, false, false, startpal, pri)); } for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { if (ty + y < th) { tiles.Add(bmp.ToTile((tx + x) * 8, (ty + y) * 8)); tilepals[tx + x, ty + y] = 0xFF; } else { tiles.Add(new byte[32]); } } } numtiles += w * h; tx += w - 1; } } } }
public static void Init() { using (Bitmap tmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed)) Palette = tmp.Palette; SonLVLColor[] pal = SonLVLColor.Load(Properties.Resources.Palette, EngineVersion.S1); for (int i = 0; i < pal.Length; i++) { Palette.Entries[i] = pal[i].RGBColor; } Palette.Entries[0] = Color.Transparent; BitmapBits bmp = new BitmapBits(Properties.Resources.GFX); BitmapBits[] sprites = new BitmapBits[bmp.Width / 24]; for (int i = 0; i < sprites.Length; i++) { sprites[i] = bmp.GetSection(i * 24, 0, 24, 24); } bmp = new BitmapBits(Properties.Resources.Font); BitmapBits[] font = new BitmapBits[11]; for (int i = 0; i < 10; i++) { font[i] = bmp.GetSection(i * 8, 0, 8, 8); } font[10] = bmp.GetSection(10 * 8, 0, 16, 8); ObjectBmps[0] = new BitmapBits(24, 24); byte ind = 1; // walls for (int p = 0; p < 4; p++) { BitmapBits tmp = new BitmapBits(sprites[0]); ObjectBmps[ind++] = tmp; for (int i = 1; i < 9; i++) { BitmapBits t2 = new BitmapBits(tmp); t2.DrawBitmapComposited(font[i], 16, 16); ObjectBmps[ind++] = t2; } sprites[0].IncrementIndexes(16); } // misc for (int i = 1; i < 7; i++) { ObjectBmps[ind++] = sprites[i]; } // "R" sprites[7].IncrementIndexes(16); ObjectBmps[ind++] = sprites[7]; // red/white ObjectBmps[ind++] = sprites[8]; // diamonds for (int p = 0; p < 4; p++) { BitmapBits tmp = new BitmapBits(sprites[9]); ObjectBmps[ind++] = tmp; sprites[9].IncrementIndexes(16); } // unused blocks ind += 3; // "ZONE" blocks for (int i = 10; i < 16; i++) { ObjectBmps[ind++] = sprites[i]; } // ring sprites[16].IncrementIndexes(16); ObjectBmps[ind++] = sprites[16]; // blue/yellow/pink/green emerald for (int p = 0; p < 4; p++) { BitmapBits tmp = new BitmapBits(sprites[19]); ObjectBmps[ind++] = tmp; sprites[19].IncrementIndexes(16); } // red emerald ObjectBmps[ind++] = sprites[17]; // gray emerald ObjectBmps[ind++] = sprites[18]; // pass-through barrier ObjectBmps[ind++] = sprites[20]; ObjectBmps[0x4A] = new BitmapBits(sprites[20]); ObjectBmps[0x4A].DrawBitmapComposited(font[10], 4, 8); ObjectBmpsNoNum = new Dictionary <byte, BitmapBits>(ObjectBmps); for (int p = 0; p < 4; p++) { BitmapBits tmp = ObjectBmps[(byte)(p * 9 + 1)]; for (int i = 1; i < 9; i++) { ObjectBmpsNoNum[(byte)(p * 9 + 1 + i)] = tmp; } } StartPosBmp = new BitmapBits(Properties.Resources.StartPos); }