Skip to content

Commit

Permalink
working on Spr color
Browse files Browse the repository at this point in the history
  • Loading branch information
UlyssesWu committed Sep 16, 2024
1 parent 7c8b1be commit 43ea3a5
Show file tree
Hide file tree
Showing 7 changed files with 376 additions and 112 deletions.
4 changes: 3 additions & 1 deletion FreeMote.PsBuild/PsbCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ public static (PSB Psb, Dictionary<string, object> Context) LoadPsbAndContextFro

//Parse
PSB psb = Parse(File.ReadAllText(inputPath), version ?? 3);
psb.FilePath = inputPath;

//Link
Dictionary<string, object> context = null;
if (!string.IsNullOrWhiteSpace(inputResJson))
Expand Down Expand Up @@ -311,7 +313,7 @@ internal static PSB Parse(string json, ushort version)
{
PSB psb = new PSB(version)
{
Encoding = Encoding
Encoding = Encoding,
};
var converter = new PsbJsonConverter();
var j = json.TrimStart();
Expand Down
5 changes: 5 additions & 0 deletions FreeMote.PsBuild/PsbDecompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,11 @@ public static void ExtractArchive(string filePath, string key, Dictionary<string

static void WriteAllBytes(string path, MemoryStream ms)
{
if (Directory.Exists(path))
{
Logger.LogError($"[ERROR] There is a folder with same name when trying to Write file.\r\n Please remove the folder if you do want to overwrite: {path}");
return;
}
EnsureDirectory(path);
using var fs = new FileStream(path, FileMode.Create);
ms.WriteTo(fs);
Expand Down
2 changes: 1 addition & 1 deletion FreeMote.Psb/Psb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public PsbSpec Platform
}
}

internal string FilePath { get; private set; }
public string FilePath { get; set; }

public string TypeId
{
Expand Down
158 changes: 158 additions & 0 deletions FreeMote.Psb/SprPainter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using FreeMote.Psb.Types;
using FastBitmapLib;

namespace FreeMote.Psb
{
public class SprPainter
{
public PSB SprData { get; set; }
public string BasePath { get; set; }
public bool JsonMode { get; set; } = true;

public int TileWidth { get; set; } = 32;
public int TileHeight { get; set; } = 32;

public int IdWidth { get; set; } = 3;

private readonly Dictionary<int, Bitmap> _textures = new Dictionary<int, Bitmap>();

//palette: each single line is a palette

public SprPainter(PSB sprData, string basePath)
{
SprData = sprData;
BasePath = basePath;
}

private void Collect()
{
if (JsonMode)
{
//read images from BasePath\__ddd.psb.m\*.png
var dirs = Directory.GetDirectories(BasePath, "*.psb.m", SearchOption.TopDirectoryOnly);
foreach (var dir in dirs)
{
var png = Path.Combine(dir, "0.png");
if (File.Exists(png))
{
var dirName = Path.GetFileName(dir);
try
{
if(int.TryParse(dirName.Substring(dirName.IndexOf('.') - IdWidth, IdWidth), out int number))
{
_textures.Add(number, new Bitmap(png));
}
}
catch (Exception ex)
{
Logger.LogWarn($"Error processing {dirName}: {ex.Message}");
}
}
}
}
else
{
Logger.LogError("JsonMode is the only supported mode for now.");
// var files = Directory.GetFiles(BasePath, "*.psb*", SearchOption.AllDirectories);
// foreach (var file in files)
// {
// var fileName = Path.GetFileName(file);
// var extension = Path.GetExtension(file).ToLowerInvariant();

// if(extension == ".m" || extension == ".psb")
// {
// try{
// if(int.TryParse(fileName.Substring(2, fileName.IndexOf('.') - 2), out int number))
// {
// PSB psb = new PSB(file);
// //TODO:
// }
// }
// catch (Exception ex)
// {
// Logger.LogWarn($"Error processing {fileName}: {ex.Message}");
// }
// }

// }
}
}

public Dictionary<string, Bitmap> Draw()
{
var result = new Dictionary<string, Bitmap>();
if (!JsonMode)
{
Logger.LogError("JsonMode is the only supported mode for now.");
return result;
}

Collect();

var sprDataList = SprData.Objects["spr_data"] as PsbList;
for (int i = 0; i < sprDataList.Count; i++)
{
var sprImage = sprDataList[i] as PsbDictionary;
var org = sprImage.Children("org") as PsbList;
var width = org[0].GetInt();
var height = org[1].GetInt();
var bitmap = new Bitmap(width, height);
var tiles = new List<SprTile>();
PsbList data = sprImage.Children("data") as PsbList;
foreach (var tileData in data)
{
var tileDataList = tileData as PsbList;
var tile = new SprTile
{
TexId = (ushort) tileDataList[0].GetInt(),
Id = (ushort) tileDataList[1].GetInt(),
X = tileDataList[2].GetInt(),
Y = tileDataList[3].GetInt()
};
tiles.Add(tile);
}

using (var f = bitmap.FastLock())
{
foreach (var tile in tiles)
{
var texId = tile.TexId;
if (!_textures.ContainsKey(texId))
{
Logger.LogError($"Tex missing: {texId} required by: {tile}");
continue;
}
var tex = _textures[texId];

var texWidth = tex.Width;
var texHeight = tex.Height; //useless, the ID is only related to width
var tilePerLine = texWidth / TileWidth;

// 0 1 2 3 ... 31
// ...
// 31*32 31*32+1 31*32+2 ... 31*32+31
//calculate source rect from tile.Id
var sourceRect = new Rectangle(
(tile.Id % tilePerLine) * TileWidth,
(tile.Id / tilePerLine) * TileHeight,
TileWidth,
TileHeight
);
var tileX = tile.X;
var tileY = tile.Y;

f.CopyRegion(tex, sourceRect, new Rectangle(tileX * TileWidth, tileY * TileHeight, TileWidth, TileHeight));
}

result.Add(i.ToString(), bitmap);
}
}

return result;
}
}
}
137 changes: 91 additions & 46 deletions FreeMote.Psb/Types/M2Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,59 +13,97 @@ class SprBlockType : BaseImageType, IPsbType

public bool IsThisType(PSB psb)
{
return psb.Objects.All(kv => kv.Value is PsbDictionary {Count: 3} dic && dic.ContainsKey("w") &&
dic.ContainsKey("h") &&
dic.ContainsKey("image"));
if (psb.Objects.All(kv => kv.Value is PsbDictionary {Count: 3} dic && dic.ContainsKey("w") &&
dic.ContainsKey("h") &&
dic.ContainsKey("image"))) return true;

if (psb.Objects is PsbDictionary { Count: 3}
dic2 && dic2.ContainsKey("w") &&
dic2.ContainsKey("h") &&
dic2.ContainsKey("image"))
{
return true;
}

return false;
}

public List<T> CollectResources<T>(PSB psb, bool deDuplication = true) where T : class, IResourceMetadata
{
var results = new List<IResourceMetadata>();
if (psb.Objects is { Count: 3 }
dic && dic.ContainsKey("w") &&
dic.ContainsKey("h") &&
dic.ContainsKey("image"))
{
var md = GenerateMetadata("0", dic, dic["image"] as PsbResource);
results.Add(md);
}
foreach (var kv in psb.Objects)
{
var dic = kv.Value as PsbDictionary;
if (dic?["image"] is PsbResource res)
var d = kv.Value as PsbDictionary;
if (d?["image"] is PsbResource res)
{
var name = kv.Key;
//test bit depth, for image it's 8bit (1 byte 1 pixel); for palette it's 32bit (4 byte 1 pixel)
var width = dic["w"].GetInt();
var height = dic["h"].GetInt();
var dataLen = res.Data.Length;
var depth = dataLen / (width * height);
PsbPixelFormat format = PsbPixelFormat.A8;
switch (depth)
{
case 1:
format = PsbPixelFormat.A8;
break;
case 4:
format = PsbPixelFormat.LeRGBA8;
break;
default:
Logger.LogWarn($"Unknown color format: {depth} bytes per pixel. Please submit sample.");
return [];
}

ImageMetadata md = new ImageMetadata()
{
Name = name,
PsbType = PsbType,
Resource = res,
Width = width,
Height = height,
Spec = PsbSpec.none,
TypeString = format.ToStringForPsb().ToPsbString()
};
var md = GenerateMetadata(kv.Key, d, res);
results.Add(md);
}
}

return results.Cast<T>().ToList();

ImageMetadata GenerateMetadata(string name, PsbDictionary dic, PsbResource res)
{
//test bit depth, for image it's 8bit (1 byte 1 pixel); for palette it's 32bit (4 byte 1 pixel)
var width = dic["w"].GetInt();
var height = dic["h"].GetInt();
var dataLen = res.Data.Length;
var depth = dataLen / (width * height);
PsbPixelFormat format = PsbPixelFormat.A8;
switch (depth)
{
case 1:
format = PsbPixelFormat.A8;
break;
case 4:
format = PsbPixelFormat.LeRGBA8;
break;
default:
Logger.LogWarn($"Unknown color format: {depth} bytes per pixel. Please submit sample.");
return null;
}

var md = new ImageMetadata()
{
Name = name,
PsbType = PsbType,
Resource = res,
Width = width,
Height = height,
Spec = PsbSpec.none,
TypeString = format.ToStringForPsb().ToPsbString()
};

return md;
}
}
}

internal struct SprTile
{
public ushort TexId;
public ushort Id;
public int X;
public int Y;

public override string ToString()
{
return $"[{TexId},{Id},{X},{Y}]";
}
}

//spr: data: 0: image no. 1:max to 1024 2:max to block[0]-1 3:max to block[1]-1
//block: [0] * [1] = total sprites in a tex
//spr tile: 32x32 tile
class SprDataType : BaseImageType, IPsbType
{
public PsbType PsbType => PsbType.SprData;
Expand All @@ -77,26 +115,33 @@ public bool IsThisType(PSB psb)

public List<T> CollectResources<T>(PSB psb, bool deDuplication = true) where T : class, IResourceMetadata
{
if (string.IsNullOrEmpty(psb.FilePath) || !File.Exists(psb.FilePath))
{
//Logger.LogHint("To get images from SprData PSB, you have to put all related PSBs in a folder first.");
return [];
}

//TODO: parse header_ex later, no sample yet.
return [];
}

public override Dictionary<string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath,
PsbExtractOption extractOption = PsbExtractOption.Original)
{
if (string.IsNullOrEmpty(psb.FilePath) || !File.Exists(psb.FilePath))
string basePath = string.Empty;
if (context.TryGet(Consts.Context_RT_SprBasePath, out string sprBasePath))
{
Logger.LogHint("To get images from SprData PSB, you have to put all related PSBs in a folder first.");
return [];
context.Context.Remove(Consts.Context_RT_SprBasePath);
if (Directory.Exists(sprBasePath))
{
basePath = sprBasePath;
}
}

if (string.IsNullOrEmpty(basePath) && !string.IsNullOrEmpty(psb.FilePath))
{
basePath = Path.GetDirectoryName(psb.FilePath);
}

var basePath = Path.GetDirectoryName(psb.FilePath);
if (string.IsNullOrEmpty(basePath) || !Directory.Exists(basePath))
{
//Logger.LogWarn("Cannot find related files. To get images from SprData PSB, you have to put all related PSBs in same folder.");
return [];
}

return [];
}
}
Expand Down
Loading

0 comments on commit 43ea3a5

Please sign in to comment.