diff --git a/.gitignore b/.gitignore
index 55a09e5f..1e2fd1a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ launchSettings.json
# Lighthouse junk
riderModule.iml
r/
+png/
/ProjectLighthouse/r/*
/ProjectLighthouse/logs/*
lighthouse.config.json
diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings
index a3b1a293..6439f6b2 100644
--- a/ProjectLighthouse.sln.DotSettings
+++ b/ProjectLighthouse.sln.DotSettings
@@ -72,12 +72,15 @@
UseExplicitType
UseExplicitType
UseExplicitType
+ BE
+ DDS
DLC
IP
LBP
MM
NAT
NP
+ PNG
PS
PSP
RPCS
@@ -98,6 +101,7 @@
True
True
True
+ True
True
True
True
diff --git a/ProjectLighthouse/Controllers/ResourcesController.cs b/ProjectLighthouse/Controllers/ResourcesController.cs
index 980d985f..f9ff9346 100644
--- a/ProjectLighthouse/Controllers/ResourcesController.cs
+++ b/ProjectLighthouse/Controllers/ResourcesController.cs
@@ -1,3 +1,4 @@
+#nullable enable
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -51,11 +52,23 @@ public class ResourcesController : ControllerBase
[ResponseCache(Duration = 86400)]
[HttpGet("/gameAssets/{hash}")]
- public IActionResult GetWebResource(string hash)
+ public IActionResult GetGameImage(string hash)
{
- string path = FileHelper.GetResourcePath(hash);
+ string path = $"png/{hash}.png";
- if (FileHelper.ResourceExists(hash) && LbpFile.FromHash(hash)?.FileType == LbpFileType.Jpeg) return this.File(IOFile.OpenRead(path), "image/jpeg");
+ if (IOFile.Exists(path))
+ {
+ return this.File(IOFile.OpenRead(path), "image/png");
+ }
+
+ LbpFile? file = LbpFile.FromHash(hash);
+ if (file != null)
+ {
+ if (ImageHelper.LbpFileToPNG(file))
+ {
+ return this.File(IOFile.OpenRead(path), "image/png");
+ }
+ }
return this.NotFound();
}
@@ -79,7 +92,7 @@ public class ResourcesController : ControllerBase
return this.UnprocessableEntity();
}
- string calculatedHash = HashHelper.Sha1Hash(file.Data).ToLower();
+ string calculatedHash = file.Hash;
if (calculatedHash != hash)
{
Logger.Log
diff --git a/ProjectLighthouse/Helpers/BinaryHelper.cs b/ProjectLighthouse/Helpers/BinaryHelper.cs
index 8e44c4ff..7d242e60 100644
--- a/ProjectLighthouse/Helpers/BinaryHelper.cs
+++ b/ProjectLighthouse/Helpers/BinaryHelper.cs
@@ -28,6 +28,8 @@ public static class BinaryHelper
while (readByte != byteToReadTo);
}
+ public static byte[] ReadToEnd(BinaryReader reader) => reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
+
public static byte[] ReadLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true)
{
long oldPosition = reader.BaseStream.Position;
diff --git a/ProjectLighthouse/Helpers/Extensions/BinaryReaderExtensions.cs b/ProjectLighthouse/Helpers/Extensions/BinaryReaderExtensions.cs
new file mode 100644
index 00000000..feacec19
--- /dev/null
+++ b/ProjectLighthouse/Helpers/Extensions/BinaryReaderExtensions.cs
@@ -0,0 +1,37 @@
+using System;
+using System.IO;
+
+namespace LBPUnion.ProjectLighthouse.Helpers.Extensions;
+
+public static class BinaryReaderExtensions
+{
+
+ #region Big Endian reading
+
+ // Yoinked from https://stackoverflow.com/questions/8620885/c-sharp-binary-reader-in-big-endian
+ public static byte[] Reverse(this byte[] b)
+ {
+ Array.Reverse(b);
+ return b;
+ }
+
+ public static ushort ReadUInt16BE(this BinaryReader binRdr) => BitConverter.ToUInt16(binRdr.ReadBytesRequired(sizeof(ushort)).Reverse(), 0);
+
+ public static short ReadInt16BE(this BinaryReader binRdr) => BitConverter.ToInt16(binRdr.ReadBytesRequired(sizeof(short)).Reverse(), 0);
+
+ public static uint ReadUInt32BE(this BinaryReader binRdr) => BitConverter.ToUInt32(binRdr.ReadBytesRequired(sizeof(uint)).Reverse(), 0);
+
+ public static int ReadInt32BE(this BinaryReader binRdr) => BitConverter.ToInt32(binRdr.ReadBytesRequired(sizeof(int)).Reverse(), 0);
+
+ public static byte[] ReadBytesRequired(this BinaryReader binRdr, int byteCount)
+ {
+ byte[] result = binRdr.ReadBytes(byteCount);
+
+ if (result.Length != byteCount) throw new EndOfStreamException($"{byteCount} bytes required from stream, but only {result.Length} returned.");
+
+ return result;
+ }
+
+ #endregion
+
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs
index 08c4caf3..d67384ef 100644
--- a/ProjectLighthouse/Helpers/FileHelper.cs
+++ b/ProjectLighthouse/Helpers/FileHelper.cs
@@ -17,7 +17,7 @@ public static class FileHelper
{
if (!ServerSettings.Instance.CheckForUnsafeFiles) return true;
- if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data);
+ if (file.FileType == LbpFileType.Unknown) return false;
return file.FileType switch
{
@@ -32,7 +32,7 @@ public static class FileHelper
LbpFileType.Jpeg => true,
LbpFileType.Png => true,
#if DEBUG
- _ => throw new ArgumentOutOfRangeException(nameof(file), $"Unhandled file type ({file.FileType}) in FileHelper.IsFileSafe()"),
+ _ => throw new ArgumentOutOfRangeException(nameof(file), $"Unhandled file type ({file.FileType}) in FileHelper.IsFileSafe()"),
#else
_ => false,
#endif
diff --git a/ProjectLighthouse/Helpers/ImageHelper.cs b/ProjectLighthouse/Helpers/ImageHelper.cs
new file mode 100644
index 00000000..691dc426
--- /dev/null
+++ b/ProjectLighthouse/Helpers/ImageHelper.cs
@@ -0,0 +1,86 @@
+#nullable enable
+using System.IO;
+using DDSReader;
+using ICSharpCode.SharpZipLib.Zip.Compression;
+using LBPUnion.ProjectLighthouse.Helpers.Extensions;
+using LBPUnion.ProjectLighthouse.Types.Files;
+
+namespace LBPUnion.ProjectLighthouse.Helpers;
+
+public static class ImageHelper
+{
+ public static bool LbpFileToPNG(LbpFile file) => LbpFileToPNG(file.Data, file.Hash, file.FileType);
+
+ public static bool LbpFileToPNG(byte[] data, string hash, LbpFileType type)
+ {
+ if (type != LbpFileType.Jpeg && type != LbpFileType.Png && type != LbpFileType.Texture) return false;
+
+ using MemoryStream ms = new(data);
+ using BinaryReader reader = new(ms);
+
+ if (type == LbpFileType.Texture) return TextureToPNG(hash, reader);
+
+ return false;
+ }
+
+ private static bool TextureToPNG(string hash, BinaryReader reader)
+ {
+ // Skip the magic (3 bytes), we already know its a texture
+ for(int i = 0; i < 3; i++) reader.ReadByte();
+
+ // This below is shamelessly stolen from ennuo's Toolkit: https://github.com/ennuo/toolkit/blob/d996ee4134740db0ee94e2cbf1e4edbd1b5ec798/src/main/java/ennuo/craftworld/utilities/Compressor.java#L40
+
+ // This byte determines the method of reading. We can only read a texture (' ') so if it's not ' ' it must be invalid.
+ if ((char)reader.ReadByte() != ' ') return false;
+
+ reader.ReadInt16(); // ?
+ short chunks = reader.ReadInt16BE();
+
+ int[] compressed = new int[chunks];
+ int[] decompressed = new int[chunks];
+
+ int decompressedSize = 0;
+ int compressedSize = 0;
+
+ for(int i = 0; i < chunks; ++i)
+ {
+ compressed[i] = reader.ReadUInt16BE();
+ decompressed[i] = reader.ReadUInt16BE();
+
+ decompressedSize += decompressed[i];
+ compressedSize += compressed[i];
+ }
+
+ using MemoryStream ms = new();
+ using BinaryWriter writer = new(ms);
+ for(int i = 0; i < chunks; ++i)
+ {
+ byte[] deflatedData = reader.ReadBytes(compressed[i]);
+ if (compressed[i] == decompressed[i])
+ {
+ writer.Write(deflatedData);
+ }
+
+ Inflater inflater = new();
+ inflater.SetInput(deflatedData);
+ byte[] inflatedData = new byte[decompressed[i]];
+ inflater.Inflate(inflatedData);
+
+ writer.Write(inflatedData);
+ }
+
+ return DDSToPNG(hash, ms.ToArray());
+ }
+
+ private static bool DDSToPNG(string hash, byte[] data)
+ {
+ using MemoryStream stream = new();
+ DDSImage image = new(data);
+
+ image.SaveAsPng(stream);
+
+ Directory.CreateDirectory("png");
+ File.WriteAllBytes($"png/{hash}.png", stream.ToArray());
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj
index 212aa906..4cc4e34e 100644
--- a/ProjectLighthouse/ProjectLighthouse.csproj
+++ b/ProjectLighthouse/ProjectLighthouse.csproj
@@ -9,6 +9,7 @@
+
@@ -21,6 +22,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/ProjectLighthouse/Types/Files/LbpFile.cs b/ProjectLighthouse/Types/Files/LbpFile.cs
index 0cd4a4d1..b3cad5e3 100644
--- a/ProjectLighthouse/Types/Files/LbpFile.cs
+++ b/ProjectLighthouse/Types/Files/LbpFile.cs
@@ -15,12 +15,16 @@ public class LbpFile
///
/// The type of file.
///
- public LbpFileType FileType;
+ public readonly LbpFileType FileType;
- public LbpFile(byte[] data)
+ public readonly string Hash;
+
+ public LbpFile(byte[] data, string? hash = null)
{
this.Data = data;
+
this.FileType = FileHelper.DetermineFileType(this.Data);
+ this.Hash = HashHelper.Sha1Hash(this.Data).ToLower();
}
public static LbpFile? FromHash(string hash)
@@ -30,6 +34,6 @@ public class LbpFile
byte[] data = File.ReadAllBytes(path);
- return new LbpFile(data);
+ return new LbpFile(data, hash);
}
}
\ No newline at end of file