NOTICE:
See the last post for the most up to date inormation. Anyway since I am nearly done, I am marking this thread as resolved.
Using the tools found at https://github.com/adventurebrew/sagaeunt to unpack Ite.rsc (Inherit the Earth: Quest for The Orb resource file) I extracted the individual resources. (See res.zip).
Wanting to convert the image resources to a more standardized format I attempted to write a SAGA_RLE1 (https://wiki.scummvm.org/index.php?t...iles/SAGA_RLE1) decompressor. Checking each file using the header description
(https://wiki.scummvm.org/index.php?t...nterface_Image I found several likely candidate resources for background images by checking whether the first two bytes would convert to a width of 320 pixels and the second two to a minum of 136. I attempted to write code to interpret SAGA_RLE1 encoding:
Code:
'This procedure attempts to read a resource as an image and convert it to a *.png file.
Private Sub ConvertToPNG(InFile As String, OutFile As String)
Dim Backtrack As New Integer
Dim Bit As New Integer
Dim BitfieldBytes() As Byte = {}
Dim Data As New Byte
Dim Data1 As New Byte
Dim Data2 As New Byte
Dim DataBytes() As Byte = {}
Dim Decoded As New List(Of Byte)
Dim ImageO As Bitmap = Nothing
Dim ImageSize As Size = Nothing
Dim MarkByte As New Byte
Dim Palette As New List(Of Color)
Dim Pixel As New Integer
Dim RunAddend As New Integer
Dim RunCount As New Integer
Using ResData As New BinaryReader(New MemoryStream(File.ReadAllBytes(InFile)))
ImageSize = New Size(ResData.ReadInt16, ResData.ReadInt16)
ResData.BaseStream.Seek(&H4%, SeekOrigin.Current)
For PaletteEntry As Integer = 0 To 255
Palette.Add(Color.FromArgb(&HFF%, ResData.ReadByte(), ResData.ReadByte(), ResData.ReadByte()))
Next PaletteEntry
While ResData.BaseStream.Position < ResData.BaseStream.Length
MarkByte = ResData.ReadByte()
Select Case MarkByte And &HC0%
Case &HC0%
RunCount = MarkByte And &H3F%
Decoded.AddRange(ResData.ReadBytes(RunCount))
Case &H80%
RunCount = (MarkByte And &H3F%) + &H3%
Data = ResData.ReadByte()
Decoded.AddRange(Enumerable.Repeat(Data, RunCount))
Case &H40%
RunCount = ((MarkByte >> &H3%) And &H7%) + &H3%
Backtrack = ResData.ReadByte()
DataBytes = Decoded.GetRange((Decoded.Count - &H1%) - Backtrack, RunCount).ToArray()
Decoded.AddRange(DataBytes)
Case &H0%
Select Case MarkByte And &H30%
Case &H30%
RunCount = (MarkByte And &HF%) + &H1%
Data1 = ResData.ReadByte()
Data2 = ResData.ReadByte()
BitfieldBytes = ResData.ReadBytes(RunCount)
For Each ByteO As Integer In BitfieldBytes
For BitIndex As Integer = &H0% To &H7%
Decoded.Add(If(((ByteO >> BitIndex) And &H1%) = &H0%, Data1, Data2))
Next BitIndex
Next ByteO
Case &H20%
RunCount = ToInt32(MarkByte And &HF%) << &H8%
RunAddend = ResData.ReadByte()
RunCount += RunAddend
Decoded.AddRange(ResData.ReadBytes(RunCount))
Case &H10%
Backtrack = (ToInt32(MarkByte And &HF%) << &H8%)
RunAddend = ResData.ReadByte()
Backtrack += RunAddend
RunCount = ResData.ReadByte()
DataBytes = Decoded.GetRange((Decoded.Count - &H1%) - Backtrack, RunCount).ToArray()
Decoded.AddRange(DataBytes)
End Select
End Select
End While
End Using
ImageO = New Bitmap(ImageSize.Width, ImageSize.Height)
Pixel = 0
For y As Integer = 0 To ImageSize.Height - 1
For x As Integer = 0 To ImageSize.Width - 1
ImageO.SetPixel(x, y, Palette(Decoded(Pixel)))
Pixel += 1
Next x
Next y
ImageO.Save($"{OutFile}.png")
End Sub
However it doesn't appear to work on any file I try. While I am going to take another look at it soon perhaps someone here will find an obvious error that has been overlooked. Any kind of feedback would be appreciated. :-)
EDIT
For some reason I am unable to upload example resources.
EDIT 4:
Thanks to a kind user on another forum (I had linked to this post there) I fixed the backtracking (markbyte & 0xC0 = 0x40) code. Still doesn't produce proper images but definitely improved.
Last edited by Peter Swinkels; Nov 8th, 2024 at 04:50 AM.
Reason: Significant update.
Thanks to some more help (elsewhere) I almost got this to work. Updated code:
Code:
'https://wiki.scummvm.org/index.php?title=SAGA/Datafiles/Background_%26_Interface_Image
'https://wiki.scummvm.org/index.php?title=SAGA/Datafiles/SAGA_RLE1
'This module's imports and settings.
Option Compare Binary
Option Explicit On
Option Infer Off
Option Strict On
Imports System
Imports System.Collections.Generic
Imports System.Convert
Imports System.Drawing
Imports System.IO
Imports System.Linq
'This module contains this program's core procedures.
Public Module CoreModule
'This procedure is started when this program is executed.
Public Sub Main()
For Each Item As String In Directory.GetFiles("D:\Other\SAGA RES DECODER\RES", "*.res")
Try
ConvertToPNG(Item, $"D:\Other\{Path.GetFileName(Item)}.png")
Catch
End Try
Next Item
End Sub
'This procedure attempts to read a resource as an image and convert it to a *.png file.
Private Sub ConvertToPNG(InFile As String, OutFile As String)
Dim Backtrack As New Integer
Dim Bit As New Integer
Dim BitfieldBytes() As Byte = {}
Dim Data As New Byte
Dim Data1 As New Byte
Dim Data2 As New Byte
Dim DataBytes() As Byte = {}
Dim Decoded As New List(Of Byte)
Dim ImageSize As Size = Nothing
Dim MarkByte As New Byte
Dim Palette As New List(Of Color)
Dim Pixel As New Integer
Dim RunAddend As New Integer
Dim RunCount As New Integer
Using ResData As New BinaryReader(New MemoryStream(File.ReadAllBytes(InFile)))
ImageSize = New Size(ResData.ReadInt16, ResData.ReadInt16)
ResData.BaseStream.Seek(&H4%, SeekOrigin.Current)
For PaletteEntry As Integer = 0 To 255
Palette.Add(Color.FromArgb(&HFF%, ResData.ReadByte(), ResData.ReadByte(), ResData.ReadByte()))
Next PaletteEntry
While ResData.BaseStream.Position < ResData.BaseStream.Length
MarkByte = ResData.ReadByte()
Select Case MarkByte And &HC0%
Case &HC0%
RunCount = MarkByte And &H3F%
Decoded.AddRange(ResData.ReadBytes(RunCount))
Case &H80%
RunCount = (MarkByte And &H3F%) + &H3%
Data = ResData.ReadByte()
Decoded.AddRange(Enumerable.Repeat(Data, RunCount))
Case &H40%
RunCount = ((MarkByte >> &H3%) And &H7%) + &H3%
Backtrack = ResData.ReadByte()
DataBytes = Decoded.GetRange((Decoded.Count - &H1%) - Backtrack, RunCount).ToArray()
Decoded.AddRange(DataBytes)
Case &H0%
Select Case MarkByte And &H30%
Case &H30%
RunCount = (MarkByte And &HF%) + &H1%
Data1 = ResData.ReadByte()
Data2 = ResData.ReadByte()
BitfieldBytes = ResData.ReadBytes(RunCount)
For Each ByteO As Integer In BitfieldBytes
For BitIndex As Integer = &H0% To &H7%
Decoded.Add(If(((ByteO >> BitIndex) And &H1%) = &H0%, Data1, Data2))
Next BitIndex
Next ByteO
Case &H20%
RunCount = ToInt32(MarkByte And &HF%) << &H8%
RunAddend = ResData.ReadByte()
RunCount += RunAddend
Decoded.AddRange(ResData.ReadBytes(RunCount))
Case &H10%
Backtrack = (ToInt32(MarkByte And &HF%) << &H8%)
RunAddend = ResData.ReadByte()
Backtrack += RunAddend
RunCount = ResData.ReadByte()
DataBytes = Decoded.GetRange((Decoded.Count - &H1%) - Backtrack, RunCount).ToArray()
Decoded.AddRange(DataBytes)
End Select
End Select
End While
End Using
DrawPixelsToPNG(Decoded, ImageSize, Palette, OutFile)
End Sub
'This procedure draws the specified pixel data onto a PNG image using the specified palette.
Private Sub DrawPixelsToPNG(Decoded As List(Of Byte), ImageSize As Size, Palette As List(Of Color), OutFile As String)
Dim ImageO As New Bitmap(ImageSize.Width, ImageSize.Height)
For y As Integer = 0 To ImageSize.Height - 1
For x As Integer = 0 To ImageSize.Width - 1
ImageO.SetPixel(x, y, Palette(Decoded((((y >> 2) * ImageSize.Width + x) << 2) + (y And 3))))
Next x
Next y
ImageO.Save(OutFile, Imaging.ImageFormat.Png)
End Sub
End Module