Skip to content

Instantly share code, notes, and snippets.

@GMMan
Created December 6, 2020 12:15
Show Gist options
  • Save GMMan/c1f0b516afdbb71769752ee06adbbd9a to your computer and use it in GitHub Desktop.
Save GMMan/c1f0b516afdbb71769752ee06adbbd9a to your computer and use it in GitHub Desktop.
Game & Watch splash converter
using System;
using System.IO;
namespace GameAndWatchBackdropToGif
{
class Program
{
static void Main(string[] args)
{
if (args.Length != 2)
{
Console.WriteLine("Usage: GameAndWatchBackdropToGif <decrypted_flash_path> <output_path>");
return;
}
int[] offsets = new int[]
{
0xcd858,
0xc58f8,
0xec318,
0xe16f8,
0xd6c78
};
using FileStream ifs = File.OpenRead(args[0]);
BinaryReader br = new BinaryReader(ifs);
Directory.CreateDirectory(args[1]);
for (int i = 0; i < offsets.Length; ++i)
{
ifs.Seek(offsets[i], SeekOrigin.Begin);
Convert(br, Path.Combine(args[1], i + ".gif"));
}
}
static void Rgb565ToRgba32(ushort pix, out byte r, out byte g, out byte b)
{
r = (byte)(((pix >> 11) * 255 + 15) / 31);
g = (byte)((((pix >> 5) & 0x3f) * 255 + 31) / 63);
b = (byte)(((pix & 0x1f) * 255 + 15) / 31);
}
static void Convert(BinaryReader br, string path)
{
using FileStream ofs = File.Create(path);
BinaryWriter bw = new BinaryWriter(ofs);
// Header
bw.Write("GIF".ToCharArray());
bw.Write("89a".ToCharArray());
// Logical screen descriptor
ushort width = br.ReadUInt16();
ushort height = br.ReadUInt16();
byte paletteSize = br.ReadByte();
br.ReadByte(); // padding
ushort[] palette = new ushort[paletteSize];
for (int i = 0; i < palette.Length; ++i)
{
palette[i] = br.ReadUInt16();
}
int gctSize = 0;
int calcGctSize = 2;
while (calcGctSize < paletteSize)
{
++gctSize;
calcGctSize <<= 1;
}
bw.Write(width);
bw.Write(height);
bw.Write((byte)((1 << 7) | gctSize));
bw.Write((byte)0);
bw.Write((byte)0);
// Global color table
for (int i = 0; i < calcGctSize; ++i)
{
if (i < palette.Length)
{
Rgb565ToRgba32(palette[i], out byte r, out byte g, out byte b);
bw.Write(r);
bw.Write(g);
bw.Write(b);
}
else
{
bw.Write((byte)0);
bw.Write((byte)0);
bw.Write((byte)0);
}
}
// Image descriptor
bw.Write((byte)0x2c);
bw.Write((ushort)0); // x
bw.Write((ushort)0); // y
bw.Write(width);
bw.Write(height);
bw.Write((byte)0); // no flags
// Frame
byte minCodeSize = br.ReadByte();
bw.Write(minCodeSize);
while (true)
{
byte blockSize = br.ReadByte();
bw.Write(blockSize);
if (blockSize == 0) break;
byte[] block = br.ReadBytes(blockSize);
bw.Write(block);
}
byte trailer = br.ReadByte();
if (trailer != 0x3b) throw new InvalidDataException("Invalid GIF trailer");
bw.Write(trailer);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment