前回、手作業で行った局ロゴデータの汎用PNG化を自動化。
局ロゴPNG化の自動化
.NET Frameworkのcscでも扱えるC#5にて作成。
コード例
logo2png.cs
using System;
using System.IO;
using System.Linq;
using System.Text;
class Program
{
static void Main(string[] args)
{
// PLTEチャンクとtRNSチャンクのバイナリを繋げて入力(536バイト)
byte[] fixedChunk = HexToBytes("00 00 01 80 50 4C…(中略)…80 80 CE B6 B1 6C");
foreach (string inputPath in args)
{
if (Path.GetExtension(inputPath).ToLower() == ".png")
{
Console.WriteLine(".pngは除外: " + inputPath);
continue;
}
string outputPath = inputPath + ".png";
using (var reader = new BinaryReader(File.OpenRead(inputPath)))
using (var writer = new BinaryWriter(File.Create(outputPath)))
{
// PNGヘッダー(8バイト)をそのままコピー
writer.Write(reader.ReadBytes(8));
while (reader.BaseStream.Position < reader.BaseStream.Length)
{
// チャンク長(4バイト)
byte[] lengthBytes = reader.ReadBytes(4);
uint length = BitConverter.ToUInt32(lengthBytes.Reverse().ToArray(), 0);
// チャンクタイプ(4バイト)
byte[] typeBytes = reader.ReadBytes(4);
string type = Encoding.ASCII.GetString(typeBytes);
// チャンクデータ(lengthバイト)+CRC(4バイト)
byte[] dataBytes = reader.ReadBytes((int)length + 4);
// チャンク全体を出力
writer.Write(lengthBytes);
writer.Write(typeBytes);
writer.Write(dataBytes);
// IHDRの後に fixedChunk を挿入
if (type == "IHDR")
{
writer.Write(fixedChunk);
}
}
}
Console.WriteLine("処理完了: " + Path.GetFileName(outputPath));
}
}
static byte[] HexToBytes(string hex)
{
string[] parts = hex.Split(' ');
byte[] bytes = new byte[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
bytes[i] = Convert.ToByte(parts[i], 16);
}
return bytes;
}
}
バイナリ部分は、前回通りのPLTEチャンクの値とtRNSチャンクの値をそのまま繋げてスペース区切りで入力する。(改行もスペースに置換)
exeに生ロゴデータのファイルをドラッグ&ドロップすると、色情報付きのPNGファイルに変換できる。
おまけ
ロゴデータが蓄積されている「LogoData」ファイルを覗くと、生ロゴデータ(PNG構造)の羅列となっている。
これを1つずつバイナリエディタで分離すると生ロゴデータが手に入るが、これも自動化。
コード例
logodata2logo.cs
using System;
using System.IO;
using System.Text;
class Program
{
static void Main(string[] args)
{
string inputPath = args[0];
string outputDir = Path.Combine(Path.GetDirectoryName(inputPath), "Logo_" + Path.GetFileName(inputPath));
Directory.CreateDirectory(outputDir);
using (var br = new BinaryReader(File.OpenRead(inputPath)))
{
// ファイルヘッダー
string type = Encoding.ASCII.GetString(br.ReadBytes(8));
if (type != "LogoData")
{
Console.WriteLine("無効なファイル形式: " + inputPath);
return;
}
uint version = br.ReadUInt32();
uint numImages = br.ReadUInt32();
for (uint i = 0; i < numImages; i++)
{
// ロゴヘッダー
ushort networkID = br.ReadUInt16();
ushort logoID = br.ReadUInt16();
ushort logoVersion = br.ReadUInt16();
byte logoType = br.ReadByte();
br.ReadByte(); // Reserved1
ushort dataSize = br.ReadUInt16();
br.ReadBytes(6); // Reserved2
ulong time = br.ReadUInt64();
// PNGデータ
byte[] pngData = br.ReadBytes(dataSize);
// CRC(読み飛ばし)
br.ReadUInt32();
// ファイル生成
string fileName = string.Format("{0:X4}_{1:X3}_{2:X3}_{3:X2}", networkID, logoID, logoVersion, logoType);
string outputPath = Path.Combine(outputDir, fileName);
File.WriteAllBytes(outputPath, pngData);
// 更新日時を設定
var fileTime = DateTime.FromFileTimeUtc((long)time * 10000000).AddHours(-9);
File.SetLastWriteTime(outputPath, fileTime);
Console.WriteLine("抽出: " + fileName);
}
}
Console.WriteLine("抽出完了: " + outputDir);
}
}
「LogoData」ファイルのドラッグ&ドロップで、生ロゴデータのファイルが「Logo_LogoData」フォルダに抽出される。これをさらにlogo2pngにかけると色情報付きのPNGファイルとなる。
(ファイル名と更新日時も、各ヘッダーの情報を基に設定。)
制作協力:Copilot