4x4 Off-Road Racing Full Screen Graphic Format
This format is used by 4x4 Off-Road Racing to store full-screen (320 × 200) images. The extension is *.pck, however the game has different graphic formats that use the same extension.
The format has no header. The size of the image and color palette are stored in the EXE, so modification is limited. The format is encoded with a basic run-length encoding. Since the format only supports 2-bit color, each byte of graphic data accounts for 4 pixels.
When decoding the graphic, you read from the beginning, loading one byte for pixel data, and another for the length of the run. Each pixel byte is 4 pixels of data, 2-bits per pixel. The format is interlaced storing all the even lines then the odd lines. There is always 1,408 bytes of padding data between the even and odd lines data to accommodate the memory structure of the CGA standard.
|BYTE||pixels||Binary representation of 4 pixels (2 bits per pixel)|
|UINT8||length||Number of times to repeat this pixel pattern.|
The following FreeBASIC code will read and display each of the graphic formats in the game with their correct EGA or CGA palette which you can specify.
' This program can decode and view the sprites, backgrounds, and full-screen ' images from the 1988 DOS game, 4x4 Off-Road Racing. It still needs a little ' work to display the sprites properly. ' Images are stored in two formats: ' ' Interlaced Format - Used only in full screen background images. ' Draws every even line until it reaches the bottom and then loops back to the ' top and draws every odd line. Has some redundant data at the end of the even ' lines. ' ' Flat Format - Used in all other images. Reads from left to right, top to ' bottom. ' ' None of the formats have headers. All image dimensions and palettes are ' stored in the EXE. Declare Function GetColor(Bit1 As Byte, Bit2 As Byte) As UByte Dim bPixels As UByte, bLength As UByte, bRepeat As UShort Dim X As Short, Y As Short, Z As Short, W As Short, lLoc As ULong Dim sWidth As UShort, bHeightJump As UByte Dim bColor As UByte Dim sName As String, sPath As String Dim Shared sPalette As String, bColors As UByte Dim aPalette(0 To 3) As UByte Screen 12 ' Available palettes include: CGA, EGA. sPalette = "EGA" ' The file to load and the path to load from. ' Take a look at Street.pck to see an Easter Egg! sName = "TITLE.PCK" sPath = "D:\\Games\\4x4\\" ' <-- Remove double slashes. Select Case UCase(sName) ' Full screen images. - Interlaced format. Case "HALLFAME.PCK", "MART.PCK", "ROAD.PCK", "RSCREEN.PCK", "SELECT.PCK",_ "SHOP.PCK", "STORES.PCK", "STREET.PCK", "TITLE.PCK", "TRUCKSCR.PCK" sWidth = 320 bHeightJump = 2 If sPalette = "CGA" Then bColors = 1 Else bColors = 0 ' The track maps. - Flat Format. Case "MAPS.PCK" sWidth = 320 bHeightJump = 1 If sPalette = "CGA" Then bColors = 1 Else bColors = 0 ' The side view of the Trucks with their masks. - Flat Format. Case "TRUCKS.PCK" sWidth = 176 bHeightJump = 1 If sPalette = "CGA" Then bColors = 1 Else bColors = 0 ' Truck driving sprites. - Flat Format. Case "HIGHLSPR.PCK", "KATACSPR.PCK", "KATANSPR.PCK", "STORMSPR.PCK",_ "TARACSPR.PCK", "TARANSPR.PCK" Select Case UCase(sName) Case "HIGHLSPR.PCK" sWidth = 44 Case "KATANSPR.PCK", "KATACSPR.PCK" sWidth = 40 Case "STORMSPR.PCK" sWidth = 48 Case "TARANSPR.PCK", "TARACSPR.PCK" sWidth = 44 End Select bHeightJump = 1 If sPalette = "CGA" Then bColors = 1 Else bColors = 2 ' Track background images. - Flat Format. Case "BAJABCK.PCK", "DVALBCK.PCK", "GEORBCK.PCK", "MICHBCK.PCK" sWidth = 576 bHeightJump = 1 ' Figure out what palette to use. Select Case UCase(sName) Case "BAJABCK.PCK" If sPalette = "CGA" Then bColors = 1 Else bColors = 2 Case "DVALBCK.PCK", "GEORBCK.PCK" If sPalette = "CGA" Then bColors = 4 Else bColors = 3 Case "MICHBCK.PCK" If sPalette = "CGA" Then bColors = 1 Else bColors = 2 End Select ' Road objects and masks. - Flat Format. Case "BAJAOBS.PCK", "DVALOBS.PCK", "GEOROBS.PCK", "MICHOBS.PCK" sWidth = 24 bHeightJump = 1 ' Figure out what palette to use. Select Case UCase(sName) Case "BAJAOBS.PCK" If sPalette = "CGA" Then bColors = 1 Else bColors = 2 Case "DVALOBS.PCK", "GEOROBS.PCK" If sPalette = "CGA" Then bColors = 4 Else bColors = 3 Case "MICHOBS.PCK" If sPalette = "CGA" Then bColors = 1 Else bColors = 2 End Select End Select ' This section is the palette lookup. ' Note, 4x4 uses the 3rd CGA color palette so it uses red rather than magenta. Select Case bColors Case 0 ' EGA - Default aPalette(0) = 0 ' Black aPalette(1) = 4 ' Red aPalette(2) = 1 ' Blue aPalette(3) = 7 ' Lt. Gray Case 1 ' CGA - Default / CGA Michigan / CGA Baja aPalette(0) = 0 ' Black aPalette(1) = 12 ' Bright Red aPalette(2) = 11 ' Bright Cyan aPalette(3) = 15 ' White Case 2 ' EGA - Baja / EGA - Michigan aPalette(0) = 0 ' Black aPalette(1) = 4 ' Red aPalette(2) = 3 ' Cyan aPalette(3) = 15 ' White Case 3 ' EGA - Death Valley / EGA - Georgia aPalette(0) = 0 ' Black aPalette(1) = 4 ' Red aPalette(2) = 3 ' Cyan aPalette(3) = 14 ' Yellow Case 4 ' CGA - Death Valley / CGA - Georgia aPalette(0) = 0 ' Black aPalette(1) = 12 ' Bright Red aPalette(2) = 10 ' Green aPalette(3) = 14 ' Yellow End Select Dim sKey As String Open sPath + sName For Binary As #1 ' Loop through the file until we hit the end. X = 0 Y = 0 Do Until EOF(1) ' Grab two bytes. The first is the pixel data, the second is the number of ' times to draw the data. Get #1, , bPixels Get #1, , bLength lLoc = lLoc + 2 ' Repeat the drawing process as many times as specified. For bRepeat = 1 To bLength W = 0 ' The binary data is stored backwards, so loop though it back to front. For Z = 3 To 0 Step -1 ' Every 2 bits is a color. Send them to our GetColor function. bColor = GetColor(Bit(bPixels, (Z * 2) + 1), Bit(bPixels, Z * 2)) ' Once the color has been deciphered, plot a pixel. Pset(X + W, Y), aPalette(bColor) W = W + 1 Next Z X = X + 4 ' When we hit the right edge, drop back to the left. If X = sWidth Then X = 0 ' Increment one or two lines based on the format. Y = Y + bHeightJump End If ' The full screen graphic format doesn't just store 1 screen worth of ' data. Some of the pixels from the middle of the image are stored at ' the end of the first pass. This data is redundant as the second pass ' will fill in all the blanks. If bHeightJump = 2 Then If Y = 204 And X = 128 Then Y = 1 X = 0 End If End If Next bRepeat Loop Close #1 Sleep ' This function converts the 2 bits into a single color. Function GetColor(Bit1 As Byte, Bit2 As Byte) As UByte Dim bCol As UByte ' First, get the index. If Bit1 = 0 and Bit2 = 0 Then bCol = 0 If Bit1 = -1 and Bit2 = 0 Then bCol = 1 If Bit1 = 0 and Bit2 = -1 Then bCol = 2 If Bit1 = -1 and Bit2 = -1 Then bCol = 3 ' Return the color. Return bCol End Function
This graphic format was reverse engineered by TheAlmightyGuru. If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)