423 lines
17 KiB
C++
423 lines
17 KiB
C++
/*
|
||
** Command & Conquer Red Alert(tm)
|
||
** Copyright 2025 Electronic Arts Inc.
|
||
**
|
||
** This program is free software: you can redistribute it and/or modify
|
||
** it under the terms of the GNU General Public License as published by
|
||
** the Free Software Foundation, either version 3 of the License, or
|
||
** (at your option) any later version.
|
||
**
|
||
** This program is distributed in the hope that it will be useful,
|
||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
** GNU General Public License for more details.
|
||
**
|
||
** You should have received a copy of the GNU General Public License
|
||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
/* $Header: g:/library/wwlib32/file/rcs/writelbm.cpp 1.1 1994/04/20 14:38:57 scott_bowen Exp $ */
|
||
/***************************************************************************
|
||
** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S **
|
||
***************************************************************************
|
||
* *
|
||
* Project Name : Write LBM File *
|
||
* *
|
||
* File Name : writelbm.c *
|
||
* *
|
||
* Programmer : Scott Bowen *
|
||
* *
|
||
* Start Date : November 18, 1991 *
|
||
* *
|
||
* Last Update : November 19, 1991 [SB] *
|
||
* *
|
||
*-------------------------------------------------------------------------*
|
||
* Functions: *
|
||
* Get_Line -- convert one plane of one row to a packed plane *
|
||
* Write_BMHD -- writes out the bit map header (LocalHeader) *
|
||
* Write_Body -- writes out compressed data in an LBM file *
|
||
* Write_CMAP -- Writes out CMAP (palette) information *
|
||
* Write_LBM_File -- Writes out a file in LBM format *
|
||
* Write_Row -- compresses and writes a row plane to .lbm file *
|
||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
||
|
||
// At the end of this file there is an IFF definition for a .LBM file.
|
||
|
||
#include <wwstd.h>
|
||
#include "iff.h"
|
||
#include "file.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
|
||
// A BitMapHeader is stored in a BMHD chunk. This structure MUST be an even size
|
||
typedef struct {
|
||
UWORD w, h; // raster width & height in pixels
|
||
UWORD x, y; // position for this image
|
||
UBYTE planes; // # source bitplanes
|
||
UBYTE masking; // masking technique
|
||
UBYTE compression; // compression algoithm
|
||
UBYTE pad1; // UNUSED. For consistency, put 0 here.
|
||
UWORD transcolor; // transparent "color number"
|
||
UBYTE xaspect, yaspect; // aspect ratio, a rational number x/y
|
||
UWORD pagewidth, pageheight; // source "page" size in pixels
|
||
} BitMapHeaderType;
|
||
|
||
|
||
// All values in LocalHeader are always the same except planes. This is set in Write_BMHD
|
||
// the WORD values must be in low-high order for compatibility.
|
||
|
||
PRIVATE BitMapHeaderType LocalHeader = {
|
||
0x4001, 0xc800, 0, 0, 0, 0, // width, height, x, y, planes, mask
|
||
1, 0, 0xFF00, 5, 6, // compress, pad1, transcolor, xasptect, yaspect
|
||
0x4001, 0xC800 }; // pagewidth, pageheight
|
||
|
||
|
||
// Used to verify that the write of the header was valid
|
||
#define BM_HEADER_SIZE (((sizeof(BitMapHeaderType) + 1) & 0xFFFE) + 8L)
|
||
|
||
|
||
/*=========================================================================*/
|
||
/* The following PRIVATE functions are in this file: */
|
||
/*=========================================================================*/
|
||
|
||
PRIVATE LONG Write_BMHD(WORD lbmhandle, WORD bitplanes);
|
||
PRIVATE LONG Write_CMAP(WORD lbmhandle, UBYTE * palette, WORD bitplanes);
|
||
PRIVATE LONG Write_BODY(WORD lbmhandle, BufferClass& buff, WORD bitplanes);
|
||
PRIVATE LONG Write_Row(WORD lbmhandle, UBYTE *buffer);
|
||
|
||
|
||
/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
|
||
|
||
/***************************************************************************
|
||
* WRITE_LBM_FILE -- Writes out a file in LBM format *
|
||
* *
|
||
* INPUT: WORD lbmhandle -- lbm file handle already opened by caller *
|
||
* BufferClass buff -- buff where MCGA picture is *
|
||
* WORD bitplane -- number of bitplanes to convert to *
|
||
* BYTE *palette -- pointer to palette for buff *
|
||
* *
|
||
* OUTPUT: Returns BOOL -- successfull or not *
|
||
* *
|
||
* WARNINGS: *
|
||
* *
|
||
* HISTORY: *
|
||
* 11/18/1991 SB : Created. *
|
||
*=========================================================================*/
|
||
|
||
PUBLIC BOOL Write_LBM_File(WORD lbmhandle, BufferClass& buff, WORD bitplanes, UBYTE *palette)
|
||
{
|
||
LONG filesize;
|
||
|
||
|
||
Seek_File(lbmhandle, 0L, SEEK_SET); // goto beginning of file
|
||
|
||
Write_File(lbmhandle, "FORM????ILBM", 12L); // First 12 bytes of all .lbm files
|
||
// size is unkown so write ????
|
||
filesize = 12L; // 4 bytes for "ILBM"
|
||
|
||
filesize += Write_BMHD(lbmhandle, bitplanes); // write out BMHD (fixed size)
|
||
filesize += Write_CMAP(lbmhandle, palette, bitplanes); // write out CMAP
|
||
|
||
// Write out the body, or compressed picture image. This size will depend
|
||
// on the compression, but the value passed back is what the compressor
|
||
// assumed was written to file
|
||
|
||
filesize += Write_BODY(lbmhandle, buff, bitplanes);
|
||
|
||
// Verify that we were able to write out the file without running out of space
|
||
if (Seek_File(lbmhandle, 0L, SEEK_END) != filesize) {
|
||
return(FALSE);
|
||
}
|
||
|
||
Seek_File(lbmhandle, 4L, SEEK_SET); // goto beginning of file
|
||
filesize = Reverse_LONG(filesize - 8L); // - 8 because of "FORM" + WORD (size)
|
||
Write_File(lbmhandle, (BYTE *) &filesize, 4L); // patch in filesize
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
/***************************************************************************
|
||
* WRITE_BMHD -- writes out the bit map header (LocalHeader) *
|
||
* *
|
||
* INPUT: WORD lbmhandle -- file handle for lbm file *
|
||
* WORD pitplanes -- number of bitplanes to write out *
|
||
* *
|
||
* OUTPUT: LONG number of bytes hopefully written out to .LBM file *
|
||
* *
|
||
* WARNINGS: *
|
||
* *
|
||
* HISTORY: *
|
||
* 11/19/1991 SB : Created. *
|
||
*=========================================================================*/
|
||
PRIVATE LONG Write_BMHD(WORD lbmhandle, WORD bitplanes)
|
||
{
|
||
LONG size;
|
||
|
||
Write_File(lbmhandle, "BMHD", 4L); // write out chunk title
|
||
size = Reverse_LONG(sizeof(LocalHeader)); // write out size of LocalHeader chunk
|
||
Write_File(lbmhandle, (BYTE *) &size, 4L);
|
||
|
||
LocalHeader.planes = bitplanes; // only nonconstant value in LocalHeader
|
||
|
||
// Make sure size is even. Return 8 = "BMHD" + size of the bitmap header structure
|
||
|
||
return(Write_File(lbmhandle, (BYTE *) &LocalHeader,
|
||
(sizeof(LocalHeader) + 1) & 0xFFFE) + 8L);
|
||
}
|
||
|
||
|
||
/***************************************************************************
|
||
* WRITE_CMAP -- Writes out CMAP (palette) information *
|
||
* *
|
||
* *
|
||
* INPUT: WORD lbmhandle -- file handle of lbm file *
|
||
* BYTE * palette -- pointer to paletter information *
|
||
* WORD bitplanes -- used to figure out size of palette *
|
||
* *
|
||
* OUTPUT: LONG number of bytes that should have been written out to .LBM. *
|
||
* *
|
||
* WARNINGS: *
|
||
* *
|
||
* HISTORY: *
|
||
* 11/19/1991 SB : Created. *
|
||
*=========================================================================*/
|
||
|
||
PRIVATE LONG Write_CMAP(WORD lbmhandle, UBYTE * palette, WORD bitplanes)
|
||
{
|
||
WORD color, r, g, b, colors;
|
||
LONG size;
|
||
UBYTE *pal_ptr;
|
||
BYTE rgb[3];
|
||
|
||
|
||
Write_File(lbmhandle, "CMAP", 4L); // write out palette info
|
||
colors = 1 << bitplanes; // colors = 2 to the bitplanes
|
||
size = Reverse_LONG(colors * 3L); // size = colors * 3 guns
|
||
|
||
Write_File(lbmhandle, (BYTE *) &size, 4L);
|
||
|
||
for (pal_ptr = palette, color = 0; color < colors; color++) { // for each color
|
||
|
||
if ((r = *pal_ptr++) != 0) { // DPaint changes allows 0 - 100 for gun values
|
||
r = (r << 2) | 0x03; // this must be converted to 0 - 256 for LBM
|
||
} // so LBM_val = (DP_val * 4) | 3 if DP_val != 0
|
||
if ((g = *pal_ptr++) != 0) {
|
||
g = (g << 2) | 0x03;
|
||
}
|
||
if ((b = *pal_ptr++) != 0) {
|
||
b = (b << 2) | 0x03;
|
||
}
|
||
rgb[0] = r; // assign gun values to an array to write out
|
||
rgb[1] = g;
|
||
rgb[2] = b;
|
||
|
||
Write_File(lbmhandle, rgb, 3L);
|
||
}
|
||
// size = colors * 3
|
||
return(((colors << 1) + colors) + 8L); // total size of CMAP 8 = "CMAP" + WORD (size)
|
||
}
|
||
|
||
|
||
/***************************************************************************
|
||
* WRITE_BODY -- writes out compressed data in an LBM file *
|
||
* *
|
||
* INPUT: WORD lbmhandle -- file handle of lbm file *
|
||
* *
|
||
* OUTPUT: LONG - number of byte written *
|
||
* *
|
||
* WARNINGS: *
|
||
* *
|
||
* HISTORY: *
|
||
* 11/19/1991 SB : Created. *
|
||
*=========================================================================*/
|
||
|
||
PRIVATE LONG Write_BODY(WORD lbmhandle, BufferClass& buff, WORD bitplanes)
|
||
{
|
||
LONG bodysize = 0;
|
||
LONG actualsize;
|
||
LONG size;
|
||
WORD planebit;
|
||
WORD line, plane;
|
||
UBYTE buffer[40];
|
||
UBYTE *buffptr;
|
||
|
||
Write_File(lbmhandle, "BODY????", 8L); // BODY chunk ID, ???? reserved for chuncksize
|
||
|
||
buffptr = (UBYTE *) buff.Get_Buffer(); // point to beginning of buff
|
||
|
||
for (line = 0; line < 200; line++) {
|
||
planebit = 1; // start with bit 1 set
|
||
|
||
for (plane = 0; plane < bitplanes; plane++) {
|
||
Pack_2_Plane(buffer, buffptr, planebit); // convert to planar
|
||
bodysize += Write_Row(lbmhandle, buffer); // write to to the BODY in the LBM
|
||
|
||
planebit <<= 1; // set next bit
|
||
}
|
||
|
||
buffptr += 320; // row size is 320
|
||
}
|
||
|
||
actualsize = bodysize + (bodysize&0x01);
|
||
|
||
if (actualsize != bodysize) {
|
||
Write_File(lbmhandle, buffer, 1); // Padd the block.
|
||
}
|
||
|
||
Seek_File(lbmhandle, -(actualsize + 4L), SEEK_CUR); // Patch in chunksize
|
||
size = Reverse_LONG(bodysize);
|
||
Write_File(lbmhandle, (BYTE *) &size ,4L);
|
||
|
||
return(actualsize + 8L); // total size of BODY, "BODY????" = 8 bytes
|
||
}
|
||
|
||
/***************************************************************************
|
||
* WRITE_ROW -- compresses and writes a row plane to .lbm file *
|
||
* *
|
||
* INPUT: WORD lbmhandle -- lbm file handle *
|
||
* UBYTE *buffer -- pointer to buffer to be written out *
|
||
* *
|
||
* OUTPUT: LONG size of chunk that should have been written out *
|
||
* *
|
||
* WARNINGS: *
|
||
* *
|
||
* HISTORY: *
|
||
* 11/19/1991 SB : Created. *
|
||
*=========================================================================*/
|
||
// this algorithm was taken from WILBM.c written by EA that was in the
|
||
// 1985 yearbook. This is the compression method that DP.EXE uses.
|
||
// Change only if DP.EXE changes.
|
||
|
||
PRIVATE LONG Write_Row(WORD lbmhandle, UBYTE *buffer)
|
||
{
|
||
WORD i;
|
||
WORD chunksize = 0;
|
||
WORD dataLength = 40; // 320 rows / 8 ( 1 plane per row)
|
||
UBYTE repCode, current, curr_plus_2;
|
||
UBYTE *buffptr;
|
||
|
||
while (dataLength) {
|
||
|
||
// If at least 2 more bytes and they are equal, then replicate
|
||
|
||
if ((dataLength >= 2) && (buffer[0] == buffer[1])) {
|
||
buffptr = buffer;
|
||
for (i = 0; (i <= 128) && (i < (dataLength - 1)); i++) {
|
||
if (*buffptr != buffptr[1]) {
|
||
break;
|
||
}
|
||
buffptr++;
|
||
}
|
||
i++;
|
||
repCode = -i + 1;
|
||
Write_File(lbmhandle, &repCode, 1L); // Write count as -count+1
|
||
Write_File(lbmhandle, buffer, 1L); // Write byte to replicate
|
||
buffer += i;
|
||
dataLength -= i;
|
||
chunksize += 2;
|
||
|
||
}
|
||
else { // Copy literally till 3 byte run or two 2 byte runs found
|
||
|
||
for (i = 0; (i <= 128) && (i < dataLength); i++) {
|
||
current = buffer[i];
|
||
curr_plus_2 = buffer[i + 2];
|
||
|
||
if (i == dataLength - 1)
|
||
continue;
|
||
if (current != buffer[i + 1])
|
||
continue;
|
||
if (i == dataLength - 2)
|
||
continue;
|
||
if (current == curr_plus_2)
|
||
break;
|
||
if (i == dataLength - 3)
|
||
continue;
|
||
if (curr_plus_2 == buffer[i + 3])
|
||
break;
|
||
}
|
||
repCode = i - 1;
|
||
Write_File(lbmhandle, &repCode, 1L); // Write count as count-1
|
||
Write_File(lbmhandle, buffer, (LONG) i); // Write 'count' bytes
|
||
buffer += i;
|
||
dataLength -= i;
|
||
chunksize += i + 1;
|
||
}
|
||
} // end while
|
||
|
||
return(chunksize);
|
||
}
|
||
|
||
|
||
#if(FALSE)
|
||
|
||
This is a definition of a DPII .LBM file.
|
||
Below this definition are differences in DPIIe .LMB files.
|
||
|
||
Created by : Scott K. Bowen Nov 18, 1991
|
||
|
||
Start with .LBM to read definition :
|
||
|
||
.LBM -> "FORM" + FILESIZE + "ILMB" + CHUNKS
|
||
|
||
BITPLANES -> (word) // number of bit planes used
|
||
BLUE -> (byte) // blue color gun value
|
||
BMHD -> "BMHD" + CHUNKSIZE + CONTEXT
|
||
BODY ->
|
||
|
||
CHUNKS -> BMHD | BODY | CMAP | DPPV | CRNG | ????
|
||
CHUNKSIZE -> (long) // size of chunk not including header or size.
|
||
CMAP -> "CMAP" + CHUNKSIZE + PALETTE_INFO
|
||
COMPRESS_METHOD -> (byte) // compression method used
|
||
CONTEXT -> WIDTH + HEIGHT + POSX + POSY + #_BITPLANES + MASKING +
|
||
COMPRESS_METHOD + PAD + TRANS_COL + XASPECT + YASPECT +
|
||
PAGEWIDTH + PAGEHEIGHT
|
||
CRNG -> // we do not use
|
||
|
||
DPPV -> // we do not use
|
||
|
||
FILESIZE -> (long) //size of file minus (sizeof(FORM) + sizeof(FILESIZE)
|
||
|
||
GREEN -> (byte) // green color gun value
|
||
|
||
HEIGHT -> (word) // of picture
|
||
|
||
MASKING -> (byte) // masking type ?
|
||
|
||
NUM_COLORS -> //number of colors used depending on format
|
||
|
||
PAGE_WIDTH -> (word) // width of page
|
||
PAGE_HEIGHT -> (word) // height of page
|
||
PALETTE_INFO-> (RED+GREEN+BLUE) @ NUM_COLORS
|
||
PAD -> (byte) // not used. used as a padding
|
||
POSX -> (word) // staring position
|
||
POSY -> (word) // staring position
|
||
|
||
RED -> (byte) // red color gun value
|
||
|
||
TRANS_COL -> (word) // transparrent color
|
||
|
||
WIDTH -> (word) // of picture
|
||
|
||
XASPECT -> (byte) // x aspect ratio
|
||
|
||
YASPECT -> (byte) // y aspect ratio
|
||
|
||
???? -> // other possibilities
|
||
|
||
|
||
Differences in DPII enhance version
|
||
|
||
.LBM -> "FORM" + FILESIZE + "PBM " + CHUNKS
|
||
DPPV -> DPPS // uses dpps instead of dppv
|
||
CHUNKS -> + TINY // add these to old definition
|
||
|
||
|
||
|
||
|
||
#endif
|
||
|