424 lines
17 KiB
C++
424 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 "iff.h"
|
|||
|
#include "file.h"
|
|||
|
#include <wwstd.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <string.h>
|
|||
|
|
|||
|
|
|||
|
// A BitMapHeader is stored in a BMHD chunk. This structure MUST be an even size
|
|||
|
typedef struct {
|
|||
|
unsigned short w, h; // raster width & height in pixels
|
|||
|
unsigned short x, y; // position for this image
|
|||
|
unsigned char planes; // # source bitplanes
|
|||
|
unsigned char masking; // masking technique
|
|||
|
unsigned char compression; // compression algoithm
|
|||
|
unsigned char pad1; // UNUSED. For consistency, put 0 here.
|
|||
|
unsigned short transcolor; // transparent "color number"
|
|||
|
unsigned char xaspect, yaspect; // aspect ratio, a rational number x/y
|
|||
|
unsigned short 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 short 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(int lbmhandle, int bitplanes);
|
|||
|
PRIVATE long Write_CMAP(int lbmhandle, unsigned char * palette, int bitplanes);
|
|||
|
PRIVATE long Write_BODY(int lbmhandle, BufferClass& buff, int bitplanes);
|
|||
|
PRIVATE long Write_Row(int lbmhandle, unsigned char *buffer);
|
|||
|
|
|||
|
|
|||
|
/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
|
|||
|
|
|||
|
/***************************************************************************
|
|||
|
* WRITE_LBM_FILE -- Writes out a file in LBM format *
|
|||
|
* *
|
|||
|
* INPUT: int lbmhandle -- lbm file handle already opened by caller *
|
|||
|
* BufferClass buff -- buff where MCGA picture is *
|
|||
|
* int bitplane -- number of bitplanes to convert to *
|
|||
|
* char *palette -- pointer to palette for buff *
|
|||
|
* *
|
|||
|
* OUTPUT: Returns BOOL -- successfull or not *
|
|||
|
* *
|
|||
|
* WARNINGS: *
|
|||
|
* *
|
|||
|
* HISTORY: *
|
|||
|
* 11/18/1991 SB : Created. *
|
|||
|
*=========================================================================*/
|
|||
|
|
|||
|
PUBLIC BOOL Write_LBM_File(int lbmhandle, BufferClass& buff, int bitplanes, unsigned char *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, (char *) &filesize, 4L); // patch in filesize
|
|||
|
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/***************************************************************************
|
|||
|
* WRITE_BMHD -- writes out the bit map header (LocalHeader) *
|
|||
|
* *
|
|||
|
* INPUT: int lbmhandle -- file handle for lbm file *
|
|||
|
* int 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(int lbmhandle, int 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, (char *) &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, (char *) &LocalHeader,
|
|||
|
(sizeof(LocalHeader) + 1) & 0xFFFE) + 8L);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/***************************************************************************
|
|||
|
* WRITE_CMAP -- Writes out CMAP (palette) information *
|
|||
|
* *
|
|||
|
* *
|
|||
|
* INPUT: int lbmhandle -- file handle of lbm file *
|
|||
|
* char * palette -- pointer to paletter information *
|
|||
|
* int 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(int lbmhandle, unsigned char * palette, int bitplanes)
|
|||
|
{
|
|||
|
int color, r, g, b, colors;
|
|||
|
long size;
|
|||
|
unsigned char *pal_ptr;
|
|||
|
char 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, (char *) &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: int lbmhandle -- file handle of lbm file *
|
|||
|
* *
|
|||
|
* OUTPUT: long - number of byte written *
|
|||
|
* *
|
|||
|
* WARNINGS: *
|
|||
|
* *
|
|||
|
* HISTORY: *
|
|||
|
* 11/19/1991 SB : Created. *
|
|||
|
*=========================================================================*/
|
|||
|
|
|||
|
PRIVATE long Write_BODY(int lbmhandle, BufferClass& buff, int bitplanes)
|
|||
|
{
|
|||
|
long bodysize = 0;
|
|||
|
long actualsize;
|
|||
|
long size;
|
|||
|
int planebit;
|
|||
|
int line, plane;
|
|||
|
unsigned char buffer[40];
|
|||
|
unsigned char *buffptr;
|
|||
|
|
|||
|
Write_File(lbmhandle, "BODY????", 8L); // BODY chunk ID, ???? reserved for chuncksize
|
|||
|
|
|||
|
buffptr = (unsigned char *) 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, (char *) &size ,4L);
|
|||
|
|
|||
|
return(actualsize + 8L); // total size of BODY, "BODY????" = 8 bytes
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************************
|
|||
|
* WRITE_ROW -- compresses and writes a row plane to .lbm file *
|
|||
|
* *
|
|||
|
* INPUT: int lbmhandle -- lbm file handle *
|
|||
|
* unsigned char *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(int lbmhandle, unsigned char *buffer)
|
|||
|
{
|
|||
|
int i;
|
|||
|
int chunksize = 0;
|
|||
|
int dataLength = 40; // 320 rows / 8 ( 1 plane per row)
|
|||
|
unsigned char repCode, current, curr_plus_2;
|
|||
|
unsigned char *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
|
|||
|
|
|||
|
|