1+ Imports System.IO
2+ Imports System.Text
3+
4+ ''' <summary>
5+ ''' Class for handling Outpost 2 palettes used in the PRT file
6+ ''' </summary>
7+ Public Class CPalette
8+
9+ # Region "Structures"
10+ ''' <summary>
11+ ''' Represents a color in the palette with RGBA components
12+ ''' </summary>
13+ Public Structure RGBQUAD
14+ Public rgbBlue As Byte
15+ Public rgbGreen As Byte
16+ Public rgbRed As Byte
17+ Public rgbReserved As Byte
18+ End Structure
19+
20+ ''' <summary>
21+ ''' Internal structure for section headers in the palette data
22+ ''' </summary>
23+ Private Structure SectionHeader
24+ Public tag As String ' 4 character tag
25+ Public sectionSize As Integer
26+ End Structure
27+ # End Region
28+
29+ # Region "Private Fields"
30+ ''' <summary>
31+ ''' The palette data (256 colors)
32+ ''' </summary>
33+ Private palData( 255 ) As RGBQUAD
34+ # End Region
35+
36+ # Region "Data Access Methods"
37+ ''' <summary>
38+ ''' Reads palette data from a binary file using a BinaryReader
39+ ''' </summary>
40+ ''' <param name="reader">BinaryReader positioned at the start of palette data</param>
41+ ''' <returns>0 on success, -1 on error</returns>
42+ Public Function ReadPaletteData(reader As BinaryReader) As Integer
43+ Dim numTagsLeft As Integer
44+ Dim sectHead As New SectionHeader()
45+ Dim i As Integer
46+ Dim temp As Byte
47+
48+ ' Initialize tag count
49+ numTagsLeft = - 2
50+
51+ ' Read all needed tags
52+ Do
53+ ' Decrease the tag count
54+ numTagsLeft = numTagsLeft - 1
55+
56+ ' Read in the section header
57+ Dim tagBytes( 3 ) As Byte
58+ reader.Read(tagBytes, 0 , 4 )
59+ sectHead.tag = Encoding.ASCII.GetString(tagBytes)
60+ sectHead.sectionSize = reader.ReadInt32()
61+
62+ ' Determine which type of section it was
63+ Select Case sectHead.tag
64+ Case "PPAL"
65+ ' Error check the section size
66+ If sectHead.sectionSize <> 1048 Then
67+ Debug.WriteLine( "CPalette: Warning! 'PPAL' section size is not 1048" )
68+ End If
69+ Case "RIFF"
70+ Debug.WriteLine( "CPalette: Warning! 'RIFF' tag unhandled" )
71+ Case "head"
72+ ' Error check the section size
73+ If sectHead.sectionSize <> 4 Then
74+ Debug.WriteLine( "CPalette: Warning! 'head' section size is not 4" )
75+ End If
76+ ' Read numTagsLeft (4 bytes of data)
77+ numTagsLeft = reader.ReadInt32()
78+ Case "data"
79+ ' Error check the section size
80+ If sectHead.sectionSize <> 1024 Then
81+ Debug.WriteLine( "CPalette: Warning! 'data' section size is not 1024" )
82+ End If
83+
84+ ' Read the palette data - read each RGBQUAD structure
85+ For i = 0 To 255
86+ palData(i).rgbBlue = reader.ReadByte()
87+ palData(i).rgbGreen = reader.ReadByte()
88+ palData(i).rgbRed = reader.ReadByte()
89+ palData(i).rgbReserved = reader.ReadByte()
90+ Next
91+ Case "pspl"
92+ Debug.WriteLine( "CPalette: Warning! 'pspl' tag unhandled" )
93+ Case "ptpl"
94+ Debug.WriteLine( "CPalette: Warning! 'ptpl' tag unhandled" )
95+ Case Else
96+ Debug.WriteLine( "CPalette: Warning! Unrecognized section tag: " & sectHead.tag)
97+ Return - 1 ' Error
98+ End Select
99+ Loop While numTagsLeft <> 0
100+
101+ ' Reverse the color components of the palette data
102+ ' (For some reason, the custom Outpost 2 format stores
103+ ' it backwards from the standard Windows format)
104+ For i = 0 To 255
105+ temp = palData(i).rgbBlue
106+ palData(i).rgbBlue = palData(i).rgbRed
107+ palData(i).rgbRed = temp
108+ Next
109+
110+ ' Return success
111+ Return 0
112+ End Function
113+
114+ ''' <summary>
115+ ''' Copies palette data to the destination array
116+ ''' </summary>
117+ ''' <param name="paletteData">Destination array for palette data</param>
118+ Friend Sub GetPaletteData( ByRef paletteData() As WinGDI.RGBQUAD)
119+ For i As Integer = 0 To 255
120+ paletteData(i) = New WinGDI.RGBQUAD() With {
121+ .rgbBlue = palData(i).rgbBlue,
122+ .rgbGreen = palData(i).rgbGreen,
123+ .rgbRed = palData(i).rgbRed,
124+ .rgbReserved = palData(i).rgbReserved
125+ }
126+ Next
127+ End Sub
128+
129+ ''' <summary>
130+ ''' Sets an entry in the palette
131+ ''' </summary>
132+ ''' <param name="index">Index of the palette entry to modify</param>
133+ ''' <param name="palEntry">New palette entry value</param>
134+ Friend Sub SetPaletteData(index As Integer , palEntry As RGBQUAD)
135+ If index > 0 AndAlso index <= 255 Then
136+ palData(index) = palEntry
137+ End If
138+ End Sub
139+ # End Region
140+
141+ # Region "Conversion Methods"
142+ ''' <summary>
143+ ''' Creates a .NET Color array from the palette
144+ ''' </summary>
145+ ''' <returns>Array of 256 Colors</returns>
146+ Public Function GetDotNetColors() As System.Drawing.Color()
147+ Dim colors( 255 ) As System.Drawing.Color
148+
149+ Debug.WriteLine( "GetDotNetColors: Converting palette to .NET colors" )
150+
151+ For i As Integer = 0 To 255
152+ ' Use FromArgb with full alpha channel (255)
153+ ' Note: The palette swap from BGRA to RGBA should already be handled in ReadPaletteData
154+ colors(i) = System.Drawing.Color.FromArgb( 255 ,
155+ palData(i).rgbRed,
156+ palData(i).rgbGreen,
157+ palData(i).rgbBlue)
158+
159+ ' Debug first few palette entries
160+ If i < 5 Then
161+ Debug.WriteLine( $"Palette[{i}] = R:{palData(i).rgbRed}, G:{palData(i).rgbGreen}, B:{palData(i).rgbBlue}" )
162+ End If
163+ Next
164+
165+ Return colors
166+ End Function
167+ # End Region
168+
169+ End Class
0 commit comments