/// <summary> /// Link /// </summary> /// <param name="psb"></param> /// <param name="resx">advanced resource json(resx.jon)</param> /// <param name="baseDir"></param> internal static void Link(this PSB psb, PsbResourceJson resx, string baseDir) { FreeMountContext context = FreeMount.CreateContext(resx.Context); var resList = psb.CollectResources(); foreach (var resxResource in resx.Resources) { //Scan for Resource var resMd = resList.FirstOrDefault(r => resxResource.Key == r.GetFriendlyName(psb.Type)); if (resMd == null && psb.Type == PsbType.Pimg) { resMd = resList.FirstOrDefault(r => resxResource.Key == Path.GetFileNameWithoutExtension(r.Name)); } if (resMd == null && uint.TryParse(resxResource.Key, out uint rid)) { resMd = resList.FirstOrDefault(r => r.Index == rid); } if (resMd == null) { Console.WriteLine($"[WARN]{resxResource.Key} is not used."); continue; } var fullPath = Path.IsPathRooted(resxResource.Value) ? resxResource.Value : Path.Combine(baseDir ?? "", resxResource.Value.Replace('/', '\\')); byte[] data = LoadImageBytes(fullPath, resMd, context); resMd.Resource.Data = data; } }
public void Link(PSB psb, FreeMountContext context, IList <string> resPaths, string baseDir = null, PsbLinkOrderBy order = PsbLinkOrderBy.Convention) { var rawResList = psb.CollectResources <AudioMetadata>(); if (order == PsbLinkOrderBy.Order) { for (int i = 0; i < rawResList.Count; i++) { var resMd = rawResList[i]; var fullPath = Path.Combine(baseDir ?? "", resPaths[i]); resMd.Link(fullPath, context); } return; } foreach (var resPath in resPaths) { var resMd = rawResList.FirstOrDefault(r => r.Name == Path.GetFileNameWithoutExtension(resPath)); if (resMd == null) { Console.WriteLine($"[WARN] {resPath} is not used."); continue; } var fullPath = Path.Combine(baseDir ?? "", resPath); resMd.Link(fullPath, context); } }
public bool TryToWave(FreeMountContext context, out List <byte[]> waveChannels) { waveChannels = null; if (context == null) { return(false); } waveChannels = new List <byte[]>(ChannelList.Count); var result = true; foreach (var channel in ChannelList) { var bytes = channel.TryToWave(context); if (bytes == null) { result = false; } else { waveChannels.Add(bytes); } } return(result); }
public void Link(PSB psb, FreeMountContext context, IDictionary <string, string> resPaths, string baseDir = null) { var rawResList = psb.CollectResources <AudioMetadata>(); foreach (var resPath in resPaths) { var fullPath = Path.Combine(baseDir ?? "", resPath.Value); var resMd = rawResList.FirstOrDefault(r => r.Name == resPath.Key); if (resMd == null) { if (uint.TryParse(resPath.Key, out var idx)) { var resource = psb.Resources.FirstOrDefault(r => r.Index == idx); if (resource != null) { resource.Data = File.ReadAllBytes(fullPath); } else { Console.WriteLine($"[WARN] {resPath.Key} is not used."); } } } else { resMd.Link(fullPath, context); } } }
internal static void LinkImages(PSB psb, FreeMountContext context, IDictionary <string, string> resources, string baseDir = null) { var resList = psb.CollectResources <ImageMetadata>(); foreach (var resxResource in resources) { //Scan for Resource var resMd = resList.FirstOrDefault(r => resxResource.Key == r.GetFriendlyName(psb.Type)); if (resMd == null && psb.Type == PsbType.Pimg) { resMd = resList.FirstOrDefault(r => resxResource.Key == Path.GetFileNameWithoutExtension(r.Name)); } if (resMd == null && resxResource.Key.StartsWith("##") && int.TryParse(resxResource.Key.Substring(2), out int rIdx)) { resMd = resList[rIdx]; } if (resMd == null && uint.TryParse(resxResource.Key, out uint rid)) { resMd = resList.FirstOrDefault(r => r.Index == rid); if (resMd == null) { //support raw palette var palResMds = resList.FindAll(r => r.Palette?.Index == rid); if (palResMds.Count > 0) { var palFullPath = Path.IsPathRooted(resxResource.Value) ? resxResource.Value : Path.Combine(baseDir ?? "", resxResource.Value.Replace('/', '\\')); var palRawData = File.ReadAllBytes(palFullPath); foreach (var palResMd in palResMds) { palResMd.PalData = palRawData; } continue; } } } if (resMd == null) { Console.WriteLine($"[WARN]{resxResource.Key} is not used."); continue; } var fullPath = Path.IsPathRooted(resxResource.Value) ? resxResource.Value : Path.Combine(baseDir ?? "", resxResource.Value.Replace('/', '\\')); resMd.Link(fullPath, context); } }
public Dictionary <string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath, PsbExtractOption extractOption = PsbExtractOption.Original) { var resources = psb.CollectResources <AudioMetadata>(); Dictionary <string, string> resDictionary = new Dictionary <string, string>(); if (extractOption == PsbExtractOption.Original) { for (int i = 0; i < psb.Resources.Count; i++) { var relativePath = psb.Resources[i].Index == null ? $"#{i}.raw" : $"{psb.Resources[i].Index}.raw"; File.WriteAllBytes( Path.Combine(dirPath, relativePath), psb.Resources[i].Data); resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}"); } } else { foreach (var resource in resources) { if (resource.ChannelList.Count == 1) { var bts = resource.ChannelList[0].TryToWave(context); var relativePath = resource.GetFileName(resource.ChannelList[0].WaveExtension); //WaveExtension may change after ToWave if (bts != null) { File.WriteAllBytes(Path.Combine(dirPath, relativePath), bts); resDictionary.Add(resource.Name, $"{name}/{relativePath}"); } } else if (resource.ChannelList.Count > 1) { for (var j = 0; j < resource.ChannelList.Count; j++) { var waveChannel = resource.ChannelList[j]; var bts = waveChannel.TryToWave(context); var relativePath = resource.GetFileName($"-{j}{waveChannel.WaveExtension}"); if (bts != null) { File.WriteAllBytes(Path.Combine(dirPath, relativePath), bts); resDictionary.Add(resource.Name, $"{name}/{relativePath}"); } } } } } return(resDictionary); }
public void Link(string fullPath, FreeMountContext context) { if (ChannelList.Count > 1) { Console.WriteLine("[WARN] Audio with multiple channels is not supported. Send me the sample for research."); } var ext = Path.GetExtension(fullPath).ToLowerInvariant(); switch (ext) { case ".at9": var at9Arch = (Atrac9ArchData)ChannelList[0]; at9Arch.Data.Data = File.ReadAllBytes(fullPath); break; case ".xwma": var xwmaArch = (XwmaArchData)ChannelList[0]; xwmaArch.ReadFromXwma(File.OpenRead(fullPath)); break; case ".wav": case ".ogg": var newArch = context.WaveToArchData(ChannelList[0].Extension, File.ReadAllBytes(fullPath), ChannelList[0].WaveExtension); if (newArch != null) { ChannelList[0].SetPsbArchData(newArch.ToPsbArchData()); } else { if (ChannelList[0].Extension == ext) { LoadFromRawFile(ChannelList[0], fullPath); } else { Console.WriteLine( $"[WARN] There is no encoder for {ChannelList[0].Extension}! {fullPath} is not used."); } } break; case ".bin": case ".raw": default: LoadFromRawFile(ChannelList[0], fullPath); break; } }
public override Dictionary <string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath, PsbExtractOption extractOption = PsbExtractOption.Original) { //Extra Extract if (extractOption == PsbExtractOption.Extract) { if (psb.Type == PsbType.Tachie) { var bitmaps = TextureCombiner.CombineTachie(psb); foreach (var kv in bitmaps) { kv.Value.Save(Path.Combine(dirPath, $"{kv.Key}{context.ImageFormat.DefaultExtension()}"), context.ImageFormat.ToImageFormat()); } } } return(base.OutputResources(psb, context, name, dirPath, extractOption)); }
public static void UnlinkImagesToFile(PSB psb, FreeMountContext context, string name, string dirPath, bool disposeResInPsb = true, PsbLinkOrderBy order = PsbLinkOrderBy.Name) { var texs = UnlinkImages(psb, order, disposeResInPsb); var texExt = context.ImageFormat.DefaultExtension(); var texFormat = context.ImageFormat.ToImageFormat(); switch (order) { case PsbLinkOrderBy.Convention: foreach (var tex in texs) { tex.Save(Path.Combine(dirPath, tex.Tag + texExt), texFormat); } break; case PsbLinkOrderBy.Name: foreach (var tex in texs) { tex.Save(Path.Combine(dirPath, $"{name}_{tex.Tag}{texExt}"), texFormat); } break; case PsbLinkOrderBy.Order: for (var i = 0; i < texs.Count; i++) { var tex = texs[i]; tex.Save(Path.Combine(dirPath, $"{i}{texExt}"), texFormat); } break; } return; }
internal static void OutputResources(PSB psb, FreeMountContext context, string filePath, PsbExtractOption extractOption = PsbExtractOption.Original, PsbImageFormat extractFormat = PsbImageFormat.png, bool useResx = true) { var name = Path.GetFileNameWithoutExtension(filePath); var dirPath = Path.Combine(Path.GetDirectoryName(filePath), name); PsbResourceJson resx = new PsbResourceJson(psb, context.Context); if (File.Exists(dirPath)) { name += "-resources"; dirPath += "-resources"; } if (!Directory.Exists(dirPath)) //ensure there is no file with same name! { if (psb.Resources.Count != 0) { Directory.CreateDirectory(dirPath); } } var resDictionary = psb.TypeHandler.OutputResources(psb, context, name, dirPath, extractOption); //MARK: We use `.resx.json` to distinguish from psbtools' `.res.json` if (useResx) { resx.Resources = resDictionary; resx.Context = context.Context; File.WriteAllText(Path.ChangeExtension(filePath, ".resx.json"), JsonConvert.SerializeObject(resx, Formatting.Indented)); } else { File.WriteAllText(Path.ChangeExtension(filePath, ".res.json"), JsonConvert.SerializeObject(resDictionary.Values.ToList(), Formatting.Indented)); } }
public Dictionary <string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath, PsbExtractOption extractOption = PsbExtractOption.Original) { return(null); }
public virtual void Link(PSB psb, FreeMountContext context, IList <string> resPaths, string baseDir = null, PsbLinkOrderBy order = PsbLinkOrderBy.Convention) { PsbResHelper.LinkImages(psb, context, resPaths, baseDir, order); }
public virtual Dictionary <string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath, PsbExtractOption extractOption = PsbExtractOption.Original) { return(PsbResHelper.OutputImageResources(psb, context, name, dirPath, extractOption)); }
public virtual void UnlinkToFile(PSB psb, FreeMountContext context, string name, string dirPath, bool outputUnlinkedPsb = true, PsbLinkOrderBy order = PsbLinkOrderBy.Name) { PsbResHelper.UnlinkImagesToFile(psb, context, name, dirPath, outputUnlinkedPsb, order); }
public virtual void Link(PSB psb, FreeMountContext context, IDictionary <string, string> resPaths, string baseDir = null) { PsbResHelper.LinkImages(psb, context, resPaths, baseDir); }
public Dictionary <string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath, PsbExtractOption extractOption = PsbExtractOption.Original) { var resources = psb.CollectResources <AudioMetadata>(); Dictionary <string, string> resDictionary = new Dictionary <string, string>(); if (extractOption == PsbExtractOption.Original) { for (int i = 0; i < psb.Resources.Count; i++) { var relativePath = psb.Resources[i].Index == null ? $"#{i}.raw" : $"{psb.Resources[i].Index}.raw"; File.WriteAllBytes( Path.Combine(dirPath, relativePath), psb.Resources[i].Data); resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}"); } } else { foreach (var resource in resources) { if (resource.ChannelList.Count == 1) { var bts = resource.ChannelList[0].TryToWave(context); var relativePath = resource.GetFileName(resource.ChannelList[0].Extension + resource.ChannelList[0].WaveExtension); //WaveExtension may change after ToWave if (bts != null) { File.WriteAllBytes(Path.Combine(dirPath, relativePath), bts); resDictionary.Add(resource.Name, $"{name}/{relativePath}"); } } else if (resource.ChannelList.Count > 1) { if (resource.Pan == PsbAudioPan.LeftRight) //load audio.vag.l.wav & audio.vag.r.wav { var left = resource.GetLeftChannel(); var relativePathL = resource.GetFileName($"{left.Extension}.l{left.WaveExtension}"); var btsL = left.TryToWave(context); if (btsL != null) { File.WriteAllBytes(Path.Combine(dirPath, relativePathL), btsL); } else { relativePathL = resource.GetFileName($"{left.Extension}.l{left.Extension}"); File.WriteAllBytes(Path.Combine(dirPath, relativePathL), left.Data.Data); resDictionary.Add(resource.Name, $"{name}/{relativePathL}"); } var right = resource.GetRightChannel(); var relativePathR = resource.GetFileName($"{right.Extension}.r{right.WaveExtension}"); var btsR = right.TryToWave(context); if (btsR != null) { File.WriteAllBytes(Path.Combine(dirPath, relativePathR), btsR); } else { relativePathR = resource.GetFileName($"{right.Extension}.r{right.Extension}"); File.WriteAllBytes(Path.Combine(dirPath, relativePathR), right.Data.Data); resDictionary.Add(resource.Name, $"{name}/{relativePathR}"); } if (btsL != null && btsR != null) { var relativePath = resource.GetFileName($"{left.Extension}{left.WaveExtension}"); resDictionary.Add(resource.Name, $"{name}/{relativePath}"); //a virtual file path } } else //not LeftRight { for (var j = 0; j < resource.ChannelList.Count; j++) //load audio.vag.1.wav etc. { var waveChannel = resource.ChannelList[j]; if (waveChannel.Data.Index == null) { Console.WriteLine($"[WARN] Channel {j} is not linked with a Resource."); continue; } var bts = waveChannel.TryToWave(context); var noStr = waveChannel.Data.Index == null ? $".@{j}" : $".#{waveChannel.Data.Index.Value}"; //TODO: handle @ - channel internal No. var relativePath = resource.GetFileName($"{waveChannel.Extension}{noStr}{waveChannel.WaveExtension}"); if (bts != null) { File.WriteAllBytes(Path.Combine(dirPath, relativePath), bts); resDictionary.Add(resource.Name, $"{name}/{relativePath}"); } } } } } } return(resDictionary); }
public static Dictionary <string, string> OutputImageResources(PSB psb, FreeMountContext context, string name, string dirPath, PsbExtractOption extractOption = PsbExtractOption.Original, PsbImageFormat extractFormat = PsbImageFormat.png) { var resources = psb.CollectResources <ImageMetadata>(); Dictionary <string, string> resDictionary = new Dictionary <string, string>(); ImageFormat pixelFormat; switch (extractFormat) { case PsbImageFormat.png: pixelFormat = ImageFormat.Png; break; default: pixelFormat = ImageFormat.Bmp; break; } if (extractOption == PsbExtractOption.Original) { for (int i = 0; i < psb.Resources.Count; i++) { var relativePath = psb.Resources[i].Index == null ? $"#{Consts.ResourceIdentifierChar}{i}.bin" : $"{psb.Resources[i].Index}.bin"; File.WriteAllBytes( Path.Combine(dirPath, relativePath), psb.Resources[i].Data); resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}"); } } else { for (int i = 0; i < resources.Count; i++) { var resource = resources[i]; //Generate Friendly Name var friendlyName = resource.GetFriendlyName(psb.Type); string relativePath = friendlyName; if (string.IsNullOrWhiteSpace(friendlyName)) { relativePath = resource.Resource.Index?.ToString() ?? $"({i})"; friendlyName = i.ToString(); } var currentExtractOption = extractOption; if (resource.Compress != PsbCompressType.Tlg && resource.Compress != PsbCompressType.ByName && (resource.Width <= 0 || resource.Height <= 0)) //impossible to extract, just keep raw { if (currentExtractOption == PsbExtractOption.Extract) { currentExtractOption = PsbExtractOption.Original; } } switch (currentExtractOption) { case PsbExtractOption.Extract: switch (extractFormat) { case PsbImageFormat.png: relativePath += ".png"; break; default: relativePath += ".bmp"; break; } relativePath = CheckPath(relativePath, i); if (resource.Compress == PsbCompressType.RL) { RL.DecompressToImageFile(resource.Data, Path.Combine(dirPath, relativePath), resource.Height, resource.Width, extractFormat, resource.PixelFormat); } else if (resource.Compress == PsbCompressType.Tlg || resource.Compress == PsbCompressType.ByName) { var bmp = context.ResourceToBitmap(resource.Compress == PsbCompressType.Tlg ? ".tlg" : Path.GetExtension(resource.Name), resource.Data); if (bmp == null) { if (resource.Compress == PsbCompressType.Tlg) //Fallback to managed TLG decoder { using var ms = new MemoryStream(resource.Data); using var br = new BinaryReader(ms); bmp = new TlgImageConverter().Read(br); bmp.Save(Path.Combine(dirPath, relativePath), pixelFormat); bmp.Dispose(); } relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name)); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); } else { bmp.Save(Path.Combine(dirPath, relativePath), pixelFormat); bmp.Dispose(); } } //else if (resource.Compress == PsbCompressType.ByName) //{ // relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name)); // File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); //} else { RL.ConvertToImageFile(resource.Data, Path.Combine(dirPath, relativePath), resource.Height, resource.Width, extractFormat, resource.PixelFormat, resource.PalData, resource.PalettePixelFormat); } break; case PsbExtractOption.Original: if (resources[i].Compress == PsbCompressType.RL) { relativePath += ".rl"; relativePath = CheckPath(relativePath, i); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); } else if (resource.Compress == PsbCompressType.Tlg) { relativePath += ".tlg"; relativePath = CheckPath(relativePath, i); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); } else { relativePath += ".raw"; relativePath = CheckPath(relativePath, i); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); } break; //case PsbExtractOption.Decompress: // relativePath += ".raw"; // relativePath = CheckPath(relativePath, i); // File.WriteAllBytes(Path.Combine(dirPath, relativePath), // resources[i].Compress == PsbCompressType.RL // ? RL.Decompress(resource.Data) // : resource.Data); // break; //case PsbExtractOption.Compress: // relativePath += ".rl"; // relativePath = CheckPath(relativePath, i); // File.WriteAllBytes(Path.Combine(dirPath, relativePath), // resources[i].Compress != PsbCompressType.RL // ? RL.Compress(resource.Data) // : resource.Data); // break; default: throw new ArgumentOutOfRangeException(nameof(currentExtractOption), currentExtractOption, null); } //Determine save name try { bool indexConflict = false; uint conflictedIndex = 0; if (resource.Resource.Index != null) //try index as name first { if (resDictionary.ContainsKey(resource.Index.ToString())) //index is used before { Console.WriteLine( $"[WARN] Resource Index {resource.Index} conflict. May be resource sharing, but may also be something wrong."); //continue; indexConflict = true; conflictedIndex = resource.Resource.Index.Value; resource.Resource.Index = null; //have another try on friendly name } } if (resource.Resource.Index == null) { if (resDictionary.ContainsKey(friendlyName)) // friendly name is also used (most likely its the same res reused), no name can be used to save { Console.WriteLine( $"[WARN] Resource Name {friendlyName} conflict. May be resource sharing, but may also be something wrong."); continue; //just skip } if (indexConflict) //index is used but friendly name is not (maybe they are different?), save using friendly name { Console.WriteLine($"[FIXED] Resource {friendlyName} is sharing same data with Index {conflictedIndex}"); } } resDictionary.Add(resource.Resource.Index == null ? friendlyName : resource.Index.ToString(), $"{name}/{relativePath}"); } catch (ArgumentException e) { throw new PsbBadFormatException(PsbBadFormatReason.Resources, "Resource Export Error: Name conflict, or Index is not specified. Try Raw export mode.", e); } } } string CheckPath(string rPath, int id) { var k = Path.GetFileNameWithoutExtension(rPath); if (resDictionary.ContainsKey(k)) { return($"{id}{Path.GetExtension(rPath)}"); } return(rPath); } return(resDictionary); }
internal static bool UseFlattenArray(this FreeMountContext context) { return(context.Context.ContainsKey(Consts.Context_UseFlattenArray) && context.Context[Consts.Context_UseFlattenArray] is bool use && use == true); }
public void Link(PSB psb, FreeMountContext context, IDictionary <string, string> resPaths, string baseDir = null) { }
public static Dictionary <string, string> OutputExtraResources(PSB psb, FreeMountContext context, string name, string dirPath, out Dictionary <string, float[]> flattenArrays, PsbExtractOption extractOption = PsbExtractOption.Original) { Dictionary <string, string> extraResDictionary = new Dictionary <string, string>(); flattenArrays = null; if (!context.UseFlattenArray() && !Consts.FlattenArrayByDefault) { extractOption = PsbExtractOption.Original; } if (extractOption == PsbExtractOption.Original) { if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } for (int i = 0; i < psb.ExtraResources.Count; i++) { var relativePath = psb.ExtraResources[i].Index == null ? $"#{Consts.ExtraResourceIdentifierChar}{i}.bin" : $"{Consts.ExtraResourceIdentifierChar}{psb.ExtraResources[i].Index}.bin"; File.WriteAllBytes( Path.Combine(dirPath, relativePath), psb.ExtraResources[i].Data); extraResDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{Consts.ExtraResourceFolderName}/{relativePath}"); } } else //Extract { flattenArrays = new Dictionary <string, float[]>(); //context.Context[Consts.Context_FlattenArray] = flattenArrays; for (int i = 0; i < psb.ExtraResources.Count; i++) { var relativePath = psb.ExtraResources[i].Index == null ? $"#{Consts.ExtraResourceIdentifierChar}{i}.bin" : $"{Consts.ExtraResourceIdentifierChar}{psb.ExtraResources[i].Index}.bin"; var data = psb.ExtraResources[i].Data; var resName = Path.GetFileNameWithoutExtension(relativePath); if (data.Length % 4 == 0) { var floats = MemoryMarshal.Cast <byte, float>(data.AsSpan()); flattenArrays.Add(resName, floats.ToArray()); //extraResDictionary.Add(resName, ""); } else { if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } File.WriteAllBytes( Path.Combine(dirPath, relativePath), psb.ExtraResources[i].Data); extraResDictionary.Add(resName, $"{name}/{Consts.ExtraResourceFolderName}/{relativePath}"); } } } return(extraResDictionary); }
public override void Link(PSB psb, FreeMountContext context, IList <string> resPaths, string baseDir = null, PsbLinkOrderBy order = PsbLinkOrderBy.Convention) { LinkImages(psb, context, resPaths, baseDir, order, true); }
internal static byte[] LoadImageBytes(string path, ResourceMetadata metadata, FreeMountContext context) { byte[] data; Bitmap image = null; var ext = Path.GetExtension(path)?.ToLowerInvariant(); if (metadata.Compress == PsbCompressType.ByName && ext != null && metadata.Name != null && metadata.Name.EndsWith(ext, true, null)) { return(File.ReadAllBytes(path)); } switch (ext) { //tlg case ".tlg" when metadata.Compress == PsbCompressType.Tlg: return(File.ReadAllBytes(path)); case ".tlg": image = context.ResourceToBitmap(".tlg", File.ReadAllBytes(path)); break; //rl case ".rl" when metadata.Compress == PsbCompressType.RL: return(File.ReadAllBytes(path)); case ".rl" when metadata.Compress == PsbCompressType.None: return(RL.Decompress(File.ReadAllBytes(path))); case ".rl": image = RL.DecompressToImage(File.ReadAllBytes(path), metadata.Height, metadata.Width, metadata.PixelFormat); break; //raw case ".raw" when metadata.Compress == PsbCompressType.None: return(File.ReadAllBytes(path)); case ".raw" when metadata.Compress == PsbCompressType.RL: return(RL.Compress(File.ReadAllBytes(path))); case ".raw": image = RL.ConvertToImage(File.ReadAllBytes(path), metadata.Height, metadata.Width, metadata.PixelFormat); break; //bin case ".bin": return(File.ReadAllBytes(path)); //image default: if (SupportedImageExt.Contains(ext)) { image = new Bitmap(path); } else if (context.SupportImageExt(ext)) { image = context.ResourceToBitmap(ext, File.ReadAllBytes(path)); } else { //MARK: No longer try to read files we don't know //return File.ReadAllBytes(path); return(null); } break; } switch (metadata.Compress) { case PsbCompressType.RL: data = RL.CompressImage(image, metadata.PixelFormat); break; case PsbCompressType.Tlg: data = context.BitmapToResource(".tlg", image); if (data == null) { var tlgPath = Path.ChangeExtension(path, ".tlg"); if (File.Exists(tlgPath)) { Console.WriteLine($"[WARN] Can not encode TLG, using {tlgPath}"); data = File.ReadAllBytes(tlgPath); } else { Console.WriteLine($"[WARN] Can not convert image to TLG: {path}"); data = File.ReadAllBytes(path); } } break; case PsbCompressType.ByName: var imgExt = Path.GetExtension(metadata.Name); if (context.SupportImageExt(imgExt)) { data = context.BitmapToResource(imgExt, image); } else { Console.WriteLine($"[WARN] Unsupported image: {path}"); data = File.ReadAllBytes(path); } break; case PsbCompressType.None: default: data = RL.GetPixelBytesFromImage(image, metadata.PixelFormat); break; } return(data); }
internal AudioMetadata GenerateAudioMetadata(PSB psb, string name, PsbDictionary voice, FreeMountContext context) { var md = new AudioMetadata { Name = name }; if (voice["file"] is PsbString fileStr) { md.FileString = fileStr; } if (voice["loopstr"] is PsbString loopstr) //Another bad design. channel[L]ist vs loop[s]tr. WTF M2?? { md.LoopStr = loopstr; } else if (voice["loopStr"] is PsbString loopStr) { md.LoopStr = loopStr; } if (md.LoopStr != null) { context.Context["loopstr"] = md.LoopStr.Value; } if (voice["loop"] is PsbNumber loopNum) { md.Loop = loopNum.IntValue; } if (voice["quality"] is PsbNumber qualityNum) { md.Quality = qualityNum.IntValue; } if (voice["type"] is PsbNumber typeNum) { md.Type = typeNum.IntValue; } if (voice["device"] is PsbNumber deviceNum) { md.Device = deviceNum.IntValue; } if (voice["channelList"] is PsbList channelList) { foreach (var channel in channelList) { if (channel is PsbDictionary channelDic) { if (context.TryGetArchData(psb, channelDic, out var archData)) { archData.PsbArchData = channelDic; md.ChannelList.Add(archData); } } } } return(md); }
public static void LinkExtraResources(PSB psb, FreeMountContext context, Dictionary <string, string> extraResourcesDictionary, Dictionary <string, float[]> flattenArrays = null, string baseDir = null) { foreach (var extraResource in psb.ExtraResources) { if (extraResource.Index == null) { Console.WriteLine("[WARN] Found Extra resource without index. Skipped."); continue; } var key = $"{Consts.ExtraResourceIdentifierChar}{extraResource.Index}"; if (flattenArrays != null && (context.UseFlattenArray() || Consts.FlattenArrayByDefault)) { if (!LinkFromFlattenArray(extraResource, key)) { if (!LinkFromFile(extraResource, key)) { Console.WriteLine($"[WARN] Extra resource {key} cannot be linked."); } } } else { if (!LinkFromFile(extraResource, key)) { Console.WriteLine($"[WARN] Extra resource {key} cannot be linked."); } } } bool LinkFromFile(PsbResource res, string key) { if (extraResourcesDictionary.ContainsKey(key) && !string.IsNullOrEmpty(extraResourcesDictionary[key])) { var path = extraResourcesDictionary[key]; var fullPath = Path.IsPathRooted(path) ? path : Path.Combine(baseDir ?? "", path.Replace('/', '\\')); if (!File.Exists(fullPath)) { return(false); } res.Data = File.ReadAllBytes(fullPath); return(true); } return(false); } bool LinkFromFlattenArray(PsbResource res, string key) { if (flattenArrays.ContainsKey(key)) { var floats = flattenArrays[key].AsSpan(); var bytes = MemoryMarshal.Cast <float, byte>(floats); res.Data = bytes.ToArray(); return(true); } return(false); } }
internal static void SetUseFlattenArray(this FreeMountContext context, bool use) { context.Context[Consts.Context_UseFlattenArray] = use; }
internal static void OutputResources(PSB psb, FreeMountContext context, string filePath, PsbExtractOption extractOption = PsbExtractOption.Original, PsbImageFormat extractFormat = PsbImageFormat.png, bool useResx = true) { var name = Path.GetFileNameWithoutExtension(filePath); var dirPath = Path.Combine(Path.GetDirectoryName(filePath), name); PsbResourceJson resx = new PsbResourceJson(psb, context.Context); if (File.Exists(dirPath)) { name += "-resources"; dirPath += "-resources"; } var extraDir = Path.Combine(dirPath, Consts.ExtraResourceFolderName); if (!Directory.Exists(dirPath)) //ensure there is no file with same name! { if (psb.Resources.Count != 0) { Directory.CreateDirectory(dirPath); } } if (psb.ExtraResources.Count > 0) { var extraDic = PsbResHelper.OutputExtraResources(psb, context, name, extraDir, out var flattenArrays, extractOption); resx.ExtraResources = extraDic; if (flattenArrays != null && flattenArrays.Count > 0) { resx.ExtraFlattenArrays = flattenArrays; } } var resDictionary = psb.TypeHandler.OutputResources(psb, context, name, dirPath, extractOption); //MARK: We use `.resx.json` to distinguish from psbtools' `.res.json` if (useResx) { resx.Resources = resDictionary; resx.Context = context.Context; string json; if (Consts.JsonArrayCollapse) { json = ArrayCollapseJsonTextWriter.SerializeObject(resx); } else { json = JsonConvert.SerializeObject(resx, Formatting.Indented); } File.WriteAllText(ChangeExtensionForOutputJson(filePath, ".resx.json"), json); } else { if (psb.ExtraResources.Count > 0) { throw new NotSupportedException("PSBv4 cannot use legacy res.json format."); } File.WriteAllText(ChangeExtensionForOutputJson(filePath, ".res.json"), JsonConvert.SerializeObject(resDictionary.Values.ToList(), Formatting.Indented)); } }
/// <summary> /// Convert <see cref="IArchData"/> to Wave /// </summary> /// <param name="archData"></param> /// <param name="context"></param> /// <returns></returns> public static byte[] TryToWave(this IArchData archData, FreeMountContext context) { return(context?.ArchDataToWave(archData.Extension, archData)); }
public void UnlinkToFile(PSB psb, FreeMountContext context, string name, string dirPath, bool outputUnlinkedPsb = true, PsbLinkOrderBy order = PsbLinkOrderBy.Name) { //TODO: }
public static void LinkImages(PSB psb, FreeMountContext context, IList <string> resPaths, string baseDir = null, PsbLinkOrderBy order = PsbLinkOrderBy.Convention, bool isExternal = false) { if (isExternal) { MotionType.MotionResourceInstrument(psb); } var rawResList = psb.CollectResources <ImageMetadata>(); if (order == PsbLinkOrderBy.Order) { for (int i = 0; i < rawResList.Count; i++) { var resMd = rawResList[i]; var fullPath = Path.Combine(baseDir ?? "", resPaths[i]); resMd.Link(fullPath, context); } return; } var resList = rawResList.ToList(); if (order == PsbLinkOrderBy.Name) { if (psb.Platform == PsbSpec.krkr) { throw new InvalidOperationException( $"Can not link by file name for krkr PSB. Please consider using {PsbLinkOrderBy.Convention}"); } resList.Sort((md1, md2) => (int)(((ImageMetadata)md1).TextureIndex ?? 0) - (int)(((ImageMetadata)md2).TextureIndex ?? 0)); } for (var i = 0; i < resPaths.Count; i++) { var resPath = resPaths[i]; var resName = Path.GetFileNameWithoutExtension(resPath); //var resMd = uint.TryParse(resName, out uint rid) // ? resList.FirstOrDefault(r => r.Index == rid) // : resList.FirstOrDefault(r => // resName == $"{r.Part}{PsbResCollector.ResourceNameDelimiter}{r.Name}"); //Scan for Resource ImageMetadata resMd = null; if (order == PsbLinkOrderBy.Name) { if (resName == null) { continue; } if (resList.Count == 1 && resPaths.Count == 1) { //If there is only one resource and one texture, we won't care about file name. resMd = resList[0]; } else { var texIdx = ImageMetadata.GetTextureIndex(resName); if (texIdx == null) { Console.WriteLine($"[WARN]{resPath} is not used since the file name cannot be recognized."); continue; } if (resList.Count <= texIdx.Value) { Console.WriteLine($"[WARN]{resPath} is not used since the tex No. is too large."); continue; } resMd = resList[(int)texIdx.Value]; } } else //if (order == PsbLinkOrderBy.Convention) { resMd = resList.FirstOrDefault(r => resName == $"{r.Part}{Consts.ResourceNameDelimiter}{r.Name}"); if (resMd == null && uint.TryParse(resName, out uint rid)) { //This Link has no support for raw palette resMd = resList.FirstOrDefault(r => r.Index == rid); } if (resMd == null && psb.Type == PsbType.Pimg) { resMd = resList.FirstOrDefault(r => resName == Path.GetFileNameWithoutExtension(r.Name)); } } if (resMd == null) { Console.WriteLine($"[WARN]{resPath} is not used."); continue; } var fullPath = Path.Combine(baseDir ?? "", resPath.Replace('/', '\\')); resMd.Link(fullPath, context); } }
internal static void OutputResources(PSB psb, FreeMountContext context, string filePath, PsbImageOption imageOption = PsbImageOption.Original, PsbImageFormat extractFormat = PsbImageFormat.Png, bool useResx = true) { var name = Path.GetFileNameWithoutExtension(filePath); var dirPath = Path.Combine(Path.GetDirectoryName(filePath), name); var resources = psb.CollectResources(); PsbResourceJson resx = new PsbResourceJson(psb, context.Context); if (File.Exists(dirPath)) { name += "-resources"; dirPath += "-resources"; } if (!Directory.Exists(dirPath)) //ensure there is no file with same name! { if (psb.Resources.Count != 0 || resources.Count != 0) { Directory.CreateDirectory(dirPath); } } Dictionary <string, string> resDictionary = new Dictionary <string, string>(); if (imageOption == PsbImageOption.Original) { for (int i = 0; i < psb.Resources.Count; i++) { var relativePath = psb.Resources[i].Index == null ? $"#{i}.bin" : $"{psb.Resources[i].Index}.bin"; File.WriteAllBytes( Path.Combine(dirPath, relativePath), psb.Resources[i].Data); resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}"); } } else { for (int i = 0; i < resources.Count; i++) { var resource = resources[i]; //Generate Friendly Name string relativePath = resource.GetFriendlyName(psb.Type); switch (imageOption) { case PsbImageOption.Extract: ImageFormat pixelFormat; switch (extractFormat) { case PsbImageFormat.Png: relativePath += ".png"; pixelFormat = ImageFormat.Png; break; default: relativePath += ".bmp"; pixelFormat = ImageFormat.Bmp; break; } relativePath = CheckPath(relativePath, i); if (resource.Compress == PsbCompressType.RL) { RL.DecompressToImageFile(resource.Data, Path.Combine(dirPath, relativePath), resource.Height, resource.Width, extractFormat, resource.PixelFormat); } else if (resource.Compress == PsbCompressType.Tlg || resource.Compress == PsbCompressType.ByName) { var bmp = context.ResourceToBitmap(resource.Compress == PsbCompressType.Tlg ? ".tlg" : Path.GetExtension(resource.Name), resource.Data); if (bmp == null) { if (resource.Compress == PsbCompressType.Tlg) //Fallback to managed TLG decoder { using (var ms = new MemoryStream(resource.Data)) using (var br = new BinaryReader(ms)) { bmp = new TlgImageConverter().Read(br); bmp.Save(Path.Combine(dirPath, relativePath), pixelFormat); bmp.Dispose(); } } relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name)); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); } else { bmp.Save(Path.Combine(dirPath, relativePath), pixelFormat); bmp.Dispose(); } } //else if (resource.Compress == PsbCompressType.ByName) //{ // relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name)); // File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); //} else { RL.ConvertToImageFile(resource.Data, Path.Combine(dirPath, relativePath), resource.Height, resource.Width, extractFormat, resource.PixelFormat); } break; case PsbImageOption.Original: if (resources[i].Compress == PsbCompressType.RL) { relativePath += ".rl"; relativePath = CheckPath(relativePath, i); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); } else if (resource.Compress == PsbCompressType.Tlg) { relativePath += ".tlg"; relativePath = CheckPath(relativePath, i); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); } else { relativePath += ".raw"; relativePath = CheckPath(relativePath, i); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data); } break; case PsbImageOption.Decompress: relativePath += ".raw"; relativePath = CheckPath(relativePath, i); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resources[i].Compress == PsbCompressType.RL ? RL.Decompress(resource.Data) : resource.Data); break; case PsbImageOption.Compress: relativePath += ".rl"; relativePath = CheckPath(relativePath, i); File.WriteAllBytes(Path.Combine(dirPath, relativePath), resources[i].Compress != PsbCompressType.RL ? RL.Compress(resource.Data) : resource.Data); break; default: throw new ArgumentOutOfRangeException(nameof(imageOption), imageOption, null); } try { resDictionary.Add(i.ToString(), $"{name}/{relativePath}"); } catch (ArgumentException e) { throw new PsbBadFormatException(PsbBadFormatReason.Resources, "There are resources with same names! Try Raw export mode.", e); } } } //MARK: We use `.resx.json` to distinguish from psbtools' `.res.json` if (useResx) { resx.Resources = resDictionary; resx.Context = context.Context; File.WriteAllText(Path.ChangeExtension(filePath, ".resx.json"), JsonConvert.SerializeObject(resx, Formatting.Indented)); } else { File.WriteAllText(Path.ChangeExtension(filePath, ".res.json"), JsonConvert.SerializeObject(resDictionary.Values.ToList(), Formatting.Indented)); } string CheckPath(string rPath, int id) { var k = Path.GetFileNameWithoutExtension(rPath); if (resDictionary.ContainsKey(k)) { return($"{id}{Path.GetExtension(rPath)}"); } return(rPath); } }