mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
430 lines
10 KiB
C++
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
|