Skip to content

Instantly share code, notes, and snippets.

@synctam
Last active October 9, 2018 22:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save synctam/4d3914fdb0284d3a6f8b36a47ef7952a to your computer and use it in GitHub Desktop.
Save synctam/4d3914fdb0284d3a6f8b36a47ef7952a to your computer and use it in GitHub Desktop.
The Tiny Bang Story pfp file packer/unpacker.
/// <summary>
/// The Tiny Bang Story pfp file packer.
/// This software is released under the GPL.
///
/// Copyright(c) お~るげーむず(仮)
/// 「How to 日本語化(Hard mode) – お~るげーむず(仮)」
/// http://ryo.game.coocan.jp/blog/game/overseasgame/jp-localize/how-to-ogj-06
/// Ported from AutoIT to C# by synctam.
/// </summary>
namespace TtbsPacker
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
internal class Program
{
[STAThread]
private static void Main(string[] args)
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.Description = "パックするフォルダを選択";
fbd.RootFolder = Environment.SpecialFolder.Desktop;
fbd.ShowNewFolderButton = false;
if (fbd.ShowDialog() != DialogResult.OK)
{
return;
}
int fspos = 0;
//// 選択フォルダのファイル一覧を取得
//// path,対象ファイル,ファイルのみ,サブフォルダも検索
string[] files = Directory.GetFiles(fbd.SelectedPath, "*", SearchOption.AllDirectories);
//// 新Packファイル作成
using (FileStream fsPack = new FileStream($@"{fbd.SelectedPath}\..\data.pfp.new", FileMode.Create, FileAccess.Write, FileShare.None))
{
using (BinaryWriter packFile = new BinaryWriter(fsPack, Encoding.UTF8))
{
//// ヘッダの書き込み
packFile.Write(Encoding.ASCII.GetBytes("PFPK"));
//// ファイル数の書き込み。
packFile.Write(files.Length);
//// アンパックしたファイルを書きこむ開始アドレス
//// ヘッダ部分4byte+ファイル数4byte
fspos += 4 + 4;
//// ファイル名の長さ1byte+ファイル名(可変)
//// +ファイルサイズ4byte+ファイル開始アドレス4byte
foreach (string path in files)
{
//// ファイル名を相対パスに変更(¥マークを / に置換え)
var archivePath = ConvertPath(fbd.SelectedPath, path);
fspos += 1 + Encoding.UTF8.GetBytes(archivePath).Length + 4 + 4;
}
var filepath = new List<string>();
foreach (var path in files)
{
//// ファイル名を相対パスに変更(¥マークを / に置換え)
var archivePath = ConvertPath(fbd.SelectedPath, path);
//// ファイル名の長さ
packFile.Write((byte)archivePath.Length);
//// ファイル名
packFile.Write(Encoding.UTF8.GetBytes(archivePath));
//// ファイル開始アドレス位置
packFile.Write(fspos);
//// ファイルサイズアドレス位置
var size = (int)new FileInfo(path).Length;
packFile.Write(size);
//// ファイルの開始位置更新
fspos += size;
}
//// アンパックしたファイルの書き込み
foreach (var path in files)
{
using (FileStream fsData = new FileStream(path, FileMode.Open, FileAccess.Read))
{
byte[] data = new byte[fsData.Length];
fsData.Read(data, 0, data.Length);
packFile.Write(data);
}
}
MessageBox.Show("パックしました", "TBS Unpacker", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
Console.Write("Hit enter key.");
Console.Read();
}
/// <summary>
/// Faile名を相対パスに変換し、セパレーターを"/"に変更する。
/// </summary>
/// <param name="selectedFolder">選択したフォルダー名</param>
/// <param name="path">ファイルのパス</param>
/// <returns>変換後のパス</returns>
private static string ConvertPath(string selectedFolder, string path)
{
return path
//// 相対パスに変換
.Replace(selectedFolder, string.Empty)
//// セパレーターを'/'に変更
.Replace('\\', '/')
//// 先頭の不要なセパレータを除去。
.TrimStart('/');
}
}
}
/// <summary>
/// The Tiny Bang Story pfp file unpacker.
/// This software is released under the GPL.
///
/// Copyright(c) お~るげーむず(仮)
/// 「How to 日本語化(Hard mode) – お~るげーむず(仮)」
/// http://ryo.game.coocan.jp/blog/game/overseasgame/jp-localize/how-to-ogj-06
/// Ported from AutoIT to C# by synctam.
/// </summary>
namespace TtbsUnpacker
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
internal class Program
{
[STAThread]
private static void Main(string[] args)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "pfp file (*.pfp)|*.pfp";
ofd.Title = "Select the file";
if (ofd.ShowDialog() != DialogResult.OK)
{
return;
}
using (BinaryReader binaryReader = new BinaryReader(new FileStream(ofd.FileName, FileMode.Open, FileAccess.Read)))
{
byte[] signature = binaryReader.ReadBytes(4);
//// ヘッダのチェックして該当ファイルならアンパック開始
if (Encoding.ASCII.GetString(signature) == "PFPK")
{
//// 文字コードを指定
Encoding enc = Encoding.UTF8;
//// ファイルの数
var fileno = binaryReader.ReadInt32();
//// ファイルを格納するための変数
var filepath = new List<string>();
var filesize = new List<int>();
var filepos = new List<int>();
//// 作業用フォルダを作成し、そこへ移動する
Directory.CreateDirectory("data");
Environment.CurrentDirectory = "data";
for (int i = 0; i < fileno; i++)
{
//// ファイル名の文字数取得
var fnmoji = binaryReader.ReadByte();
//// ファイル名をフォルダ構成毎取得
filepath.Add(enc.GetString(binaryReader.ReadBytes(fnmoji)));
//// ファイルの開始位置取得
filepos.Add(binaryReader.ReadInt32());
//// ファイルサイズを取得
filesize.Add(binaryReader.ReadInt32());
}
for (int i = 0; i < fileno; i++)
{
//// ファイル名のスラッシュを¥マークに変換
var fw = filepath[i].Replace('/', '\\');
//// ファイルの開始位置へ移動
binaryReader.BaseStream.Seek(filepos[i], SeekOrigin.Begin);
//// data.pfpよりデータを読み取る
var filedata = binaryReader.ReadBytes(filesize[i]);
Console.WriteLine($"Extracted file: {fw} Size({filesize[i]})");
//// フォルダーの有無を確認しなければ作成
var folderName = Path.GetDirectoryName(fw);
if (!Directory.Exists(folderName))
{
Directory.CreateDirectory(folderName);
}
//// 書き込み用ファイルをオープン(フォルダ毎作成する設定)
using (BinaryWriter binaryWriter = new BinaryWriter(new FileStream(fw, FileMode.OpenOrCreate, FileAccess.Write), enc))
{
//// 書き込み
binaryWriter.Write(filedata);
}
}
MessageBox.Show("全てのファイルを書きだしました", "TBS Unpacker", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
string msg = BitConverter.ToString(signature);
MessageBox.Show("The selected file is not *.pfp.\r\nPlease restart it.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Console.Write("Hit enter key.");
Console.Read();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment