Results 1 to 2 of 2

Thread: [RESOLVED] SAGA_RLE1 decoder

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,909

    Resolved [RESOLVED] SAGA_RLE1 decoder

    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 2:
    Probably because those resources are binary files. I guess a Google Drive link will have to do: https://drive.google.com/file/d/1CfT...ew?usp=sharing

    EDIT 3:
    Anyone wishing to do a virusscan on the files: https://www.virustotal.com/gui/home/upload

    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.
    Attached Files Attached Files
    Last edited by Peter Swinkels; Nov 8th, 2024 at 04:50 AM. Reason: Significant update.

  2. #2

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,909

    Re: SAGA_RLE1 decoder

    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width