Files
scummvm/engines/ags/shared/util/compress.cpp
T
2021-02-16 19:45:02 -08:00

430 lines
10 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ags/shared/ac/common.h" // quit, update_polled_stuff
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/util/compress.h"
#include "ags/shared/util/lzw.h"
#include "ags/shared/util/misc.h"
#include "ags/shared/util/stream.h"
#if AGS_PLATFORM_ENDIAN_BIG
#include "ags/shared/util/bbop.h"
#endif
namespace AGS3 {
using namespace AGS::Shared;
void cpackbitl(const uint8_t *line, int size, Stream *out) {
int cnt = 0; // bytes encoded
while (cnt < size) {
int i = cnt;
int j = i + 1;
int jmax = i + 126;
if (jmax >= size)
jmax = size - 1;
if (i == size - 1) { //................last byte alone
out->WriteInt8(0);
out->WriteInt8(line[i]);
cnt++;
} else if (line[i] == line[j]) { //....run
while ((j < jmax) && (line[j] == line[j + 1]))
j++;
out->WriteInt8(i - j);
out->WriteInt8(line[i]);
cnt += j - i + 1;
} else { //.............................sequence
while ((j < jmax) && (line[j] != line[j + 1]))
j++;
out->WriteInt8(j - i);
out->WriteArray(line + i, j - i + 1, 1);
cnt += j - i + 1;
}
} // end while
}
void cpackbitl16(const uint16_t *line, int size, Stream *out) {
int cnt = 0; // bytes encoded
while (cnt < size) {
int i = cnt;
int j = i + 1;
int jmax = i + 126;
if (jmax >= size)
jmax = size - 1;
if (i == size - 1) { //................last byte alone
out->WriteInt8(0);
out->WriteInt16(line[i]);
cnt++;
} else if (line[i] == line[j]) { //....run
while ((j < jmax) && (line[j] == line[j + 1]))
j++;
out->WriteInt8(i - j);
out->WriteInt16(line[i]);
cnt += j - i + 1;
} else { //.............................sequence
while ((j < jmax) && (line[j] != line[j + 1]))
j++;
out->WriteInt8(j - i);
out->WriteArray(line + i, j - i + 1, 2);
cnt += j - i + 1;
}
} // end while
}
void cpackbitl32(const uint32_t *line, int size, Stream *out) {
int cnt = 0; // bytes encoded
while (cnt < size) {
int i = cnt;
int j = i + 1;
int jmax = i + 126;
if (jmax >= size)
jmax = size - 1;
if (i == size - 1) { //................last byte alone
out->WriteInt8(0);
out->WriteInt32(line[i]);
cnt++;
} else if (line[i] == line[j]) { //....run
while ((j < jmax) && (line[j] == line[j + 1]))
j++;
out->WriteInt8(i - j);
out->WriteInt32(line[i]);
cnt += j - i + 1;
} else { //.............................sequence
while ((j < jmax) && (line[j] != line[j + 1]))
j++;
out->WriteInt8(j - i);
out->WriteArray(line + i, j - i + 1, 4);
cnt += j - i + 1;
}
} // end while
}
void csavecompressed(Stream *out, const unsigned char *tobesaved, const color pala[256]) {
int widt, hit;
widt = *tobesaved++;
widt += (*tobesaved++) * 256;
hit = *tobesaved++;
hit += (*tobesaved++) * 256;
// Those were originally written as shorts, although they are ints
out->WriteInt16(widt);
out->WriteInt16(hit);
unsigned char *ress = (unsigned char *)malloc(widt + 1);
int ww;
for (ww = 0; ww < hit; ww++) {
for (int ss = 0; ss < widt; ss++)
(*ress++) = (*tobesaved++);
ress -= widt;
cpackbitl(ress, widt, out);
}
for (ww = 0; ww < 256; ww++) {
out->WriteInt8(pala[ww].r);
out->WriteInt8(pala[ww].g);
out->WriteInt8(pala[ww].b);
}
free(ress);
}
int cunpackbitl(uint8_t *line, int size, Stream *in) {
int n = 0; // number of bytes decoded
while (n < size) {
int ix = in->ReadByte(); // get index byte
if (in->HasErrors())
break;
int8 cx = ix;
if (cx == -128)
cx = 0;
if (cx < 0) { //.............run
int i = 1 - cx;
char ch = in->ReadInt8();
while (i--) {
// test for buffer overflow
if (n >= size)
return -1;
line[n++] = ch;
}
} else { //.....................seq
int i = cx + 1;
while (i--) {
// test for buffer overflow
if (n >= size)
return -1;
line[n++] = in->ReadByte();
}
}
}
return in->HasErrors() ? -1 : 0;
}
int cunpackbitl16(uint16_t *line, int size, Stream *in) {
int n = 0; // number of bytes decoded
while (n < size) {
int ix = in->ReadByte(); // get index byte
if (in->HasErrors())
break;
int8 cx = ix;
if (cx == -128)
cx = 0;
if (cx < 0) { //.............run
int i = 1 - cx;
unsigned short ch = in->ReadInt16();
while (i--) {
// test for buffer overflow
if (n >= size)
return -1;
line[n++] = ch;
}
} else { //.....................seq
int i = cx + 1;
while (i--) {
// test for buffer overflow
if (n >= size)
return -1;
line[n++] = in->ReadInt16();
}
}
}
return in->HasErrors() ? -1 : 0;
}
int cunpackbitl32(uint32_t *line, int size, Stream *in) {
int n = 0; // number of bytes decoded
while (n < size) {
int ix = in->ReadByte(); // get index byte
if (in->HasErrors())
break;
int8 cx = ix;
if (cx == -128)
cx = 0;
if (cx < 0) { //.............run
int i = 1 - cx;
unsigned int ch = in->ReadInt32();
while (i--) {
// test for buffer overflow
if (n >= size)
return -1;
line[n++] = ch;
}
} else { //.....................seq
int i = cx + 1;
while (i--) {
// test for buffer overflow
if (n >= size)
return -1;
line[n++] = (unsigned int)in->ReadInt32();
}
}
}
return in->HasErrors() ? -1 : 0;
}
//=============================================================================
const char *lztempfnm = "~aclzw.tmp";
void save_lzw(Stream *out, const Bitmap *bmpp, const color *pall) {
// First write original bitmap into temporary file
Stream *lz_temp_s = ci_fopen(lztempfnm, kFile_CreateAlways, kFile_Write);
lz_temp_s->WriteInt32(bmpp->GetWidth() * bmpp->GetBPP());
lz_temp_s->WriteInt32(bmpp->GetHeight());
lz_temp_s->WriteArray(bmpp->GetData(), bmpp->GetLineLength(), bmpp->GetHeight());
delete lz_temp_s;
// Now open same file for reading, and begin writing compressed data into required output stream
lz_temp_s = ci_fopen(lztempfnm);
soff_t temp_sz = lz_temp_s->GetLength();
out->SafeWriteArray(&pall[0], PALETTE_COUNT);
out->WriteInt32(temp_sz);
soff_t gobacto = out->GetPosition();
// reserve space for compressed size
out->WriteInt32(temp_sz);
lzwcompress(lz_temp_s, out);
soff_t toret = out->GetPosition();
out->Seek(gobacto, kSeekBegin);
soff_t compressed_sz = (toret - gobacto) - 4;
out->WriteInt32(compressed_sz); // write compressed size
// Delete temp file
delete lz_temp_s;
::remove(lztempfnm);
// Seek back to the end of the output stream
out->Seek(toret, kSeekBegin);
}
void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, color *pall) {
soff_t uncompsiz;
int *loptr;
unsigned char *membuffer;
int arin;
in->SafeReadArray(&pall[0], PALETTE_COUNT);
maxsize = in->ReadInt32();
uncompsiz = in->ReadInt32();
uncompsiz += in->GetPosition();
outbytes = 0;
putbytes = 0;
update_polled_stuff_if_runtime();
membuffer = lzwexpand_to_mem(in);
update_polled_stuff_if_runtime();
loptr = (int *)&membuffer[0];
membuffer += 8;
#if AGS_PLATFORM_ENDIAN_BIG
loptr[0] = BBOp::SwapBytesInt32(loptr[0]);
loptr[1] = BBOp::SwapBytesInt32(loptr[1]);
int bitmapNumPixels = loptr[0] * loptr[1] / dst_bpp;
switch (dst_bpp) { // bytes per pixel!
case 1:
{
// all done
break;
}
case 2:
{
short *sp = (short *)membuffer;
for (int i = 0; i < bitmapNumPixels; ++i) {
sp[i] = BBOp::SwapBytesInt16(sp[i]);
}
// all done
break;
}
case 4:
{
int *ip = (int *)membuffer;
for (int i = 0; i < bitmapNumPixels; ++i) {
ip[i] = BBOp::SwapBytesInt32(ip[i]);
}
// all done
break;
}
}
#endif // AGS_PLATFORM_ENDIAN_BIG
update_polled_stuff_if_runtime();
Bitmap *bmm = BitmapHelper::CreateBitmap((loptr[0] / dst_bpp), loptr[1], dst_bpp * 8);
if (bmm == nullptr)
quit("!load_room: not enough memory to load room background");
update_polled_stuff_if_runtime();
bmm->Acquire();
for (arin = 0; arin < loptr[1]; arin++)
memcpy(&bmm->GetScanLineForWriting(arin)[0], &membuffer[arin * loptr[0]], loptr[0]);
bmm->Release();
update_polled_stuff_if_runtime();
free(membuffer - 8);
if (in->GetPosition() != uncompsiz)
in->Seek(uncompsiz, kSeekBegin);
update_polled_stuff_if_runtime();
*dst_bmp = bmm;
}
void savecompressed_allegro(Stream *out, const Bitmap *bmpp, const color *pall) {
unsigned char *wgtbl = (unsigned char *)malloc(bmpp->GetWidth() * bmpp->GetHeight() + 4);
short *sss = (short *)wgtbl;
sss[0] = bmpp->GetWidth();
sss[1] = bmpp->GetHeight();
memcpy(&wgtbl[4], bmpp->GetData(), bmpp->GetWidth() * bmpp->GetHeight());
csavecompressed(out, wgtbl, pall);
free(wgtbl);
}
void loadcompressed_allegro(Stream *in, Bitmap **bimpp, color *pall) {
short widd, hitt;
int ii;
widd = in->ReadInt16();
hitt = in->ReadInt16();
Bitmap *bim = BitmapHelper::CreateBitmap(widd, hitt, 8);
if (bim == nullptr)
quit("!load_room: not enough memory to decompress masks");
for (ii = 0; ii < hitt; ii++) {
cunpackbitl(&bim->GetScanLineForWriting(ii)[0], widd, in);
if (ii % 20 == 0)
update_polled_stuff_if_runtime();
}
in->Seek(768); // skip palette
*bimpp = bim;
}
} // namespace AGS3