Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,217 @@
/* 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 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/>.
*
*/
#include "m4/graphics/gr_buff.h"
#include "m4/graphics/gr_pal.h"
#include "m4/gui/gui_vmng_core.h"
#include "m4/core/errors.h"
#include "m4/mem/memman.h"
namespace M4 {
GrBuff::GrBuff(int32 _w, int32 _h) {
w = _w;
h = _h;
x_off = y_off = 0;
pitch = _w;
height = _h;
alloc_pixmap();
}
GrBuff::GrBuff(int32 _w, int32 _h, int32 _x_off, int32 _y_off, int32 _pitch, int32 _height) {
w = _w;
h = _h;
x_off = _x_off;
y_off = _y_off;
pitch = _pitch;
height = _height;
alloc_pixmap();
}
GrBuff::~GrBuff() {
if (pixmap)
DisposeHandle(pixmap);
}
void GrBuff::lock() {
if (!pixmap)
return;
HLock(pixmap);
}
void GrBuff::release() {
if (pixmap)
HUnLock(pixmap);
}
void GrBuff::alloc_pixmap() {
pixmap = NewHandle(pitch * height, "pixmap");
HLock(pixmap);
memset(*pixmap, __BLACK, pitch * height);
HUnLock(pixmap);
}
uint8 *GrBuff::get_pixmap() {
if (pixmap) {
lock();
return (uint8 *)*pixmap;
}
return nullptr;
}
Buffer *GrBuff::get_buffer() {
if (pixmap) {
lock();
dummy.data = (uint8 *)*pixmap;
dummy.w = w;
dummy.h = h;
dummy.encoding = 0;
dummy.stride = pitch;
return &dummy;
}
return nullptr;
}
void GrBuff::refresh_video(int32 scrnX, int32 scrnY, int32 x1, int32 y1, int32 x2, int32 y2) {
vmng_refresh_video(scrnX, scrnY, x1, y1, x2, y2, get_buffer());
}
int32 gr_buffer_free(Buffer *buf) {
buf->w = buf->h = buf->stride = 0;
if (buf->data != nullptr) {
mem_free((char *)buf->data);
buf->data = nullptr;
return true;
}
error_show(FL, 'BUF!');
return false;
}
byte *gr_buffer_pointer(Buffer *buf, int32 x, int32 y) {
if (!buf || !buf->data || y < 0 || x < 0) {
error_show(FL, 'BUF!', "buffer_pointer x,y = %d,%d", x, y);
return nullptr;
}
return (byte *)(buf->data + x + (y * buf->stride));
}
const byte *gr_buffer_pointer(const Buffer *buf, int32 x, int32 y) {
if (!buf || !buf->data || y < 0 || x < 0) {
error_show(FL, 'BUF!', "buffer_pointer x,y = %d,%d", x, y);
return nullptr;
}
return (byte *)(buf->data + x + (y * buf->stride));
}
int32 gr_buffer_init(Buffer *buf, const char *name, int32 w, int32 h) {
if (buf->data)
error_show(FL, 'BUFR', "buffer_init %s", name);
buf->w = w;
buf->h = h;
buf->stride = w;
buf->data = (uint8 *)mem_alloc(buf->stride * h, name);
if (buf->data == nullptr)
error_show(FL, 'OOM!', "buffer: %s - w:%d h:%d bytes:%d", name, buf->stride, h, buf->stride * h);
memset(buf->data, 0, buf->stride * h);
return (true);
}
bool gr_buffer_rect_copy_2(const Buffer *from, Buffer *to, int32 sx, int32 sy,
int32 dx, int32 dy, int32 w, int32 h) {
// stupid check for no data
if (!from || !to || !from->data || !to->data)
error_show(FL, 'BUF!', "buff_rect_copy2");
// CKB: if height is greater than source height, truncate!
if (h > from->h)
h = from->h;
// if source x,y or dest x,y won't touch dest or source buffers, we're done
if ((sx > from->w) || (sy > from->h) || (dx > to->w) || (dy > to->h))
return true;
// if dest request intersects dest buffer, clip dest request
if ((dx + w) > to->w)
w = to->w - dx;
if ((dy + h) > to->h)
h = to->h - dy;
// if our dest request is too small, we're done
if ((w < 1) || (h < 1))
return true;
// initialize pointers
const byte *src = gr_buffer_pointer(from, sx, sy);
byte *dest = gr_buffer_pointer(to, dx, dy);
// get stride
int32 sIncr = from->stride;
int32 dIncr = to->stride;
// copy one to d'other
for (int i = 0; i < h; i++, dest += dIncr, src += sIncr)
memmove(dest, src, w);
return true;
}
bool gr_buffer_rect_copy(Buffer *from, Buffer *to, int32 x, int32 y, int32 w, int32 h) {
return (gr_buffer_rect_copy_2(from, to, x, y, x, y, w, h));
}
int32 gr_buffer_rect_fill(Buffer *target, int32 x1, int32 y1, int32 w, int32 h) {
const byte color = gr_color_get_current();
// if no data, bad.
if (!target || !target->data)
error_show(FL, 'BUF!', "buffer_rect_fill");
// if nothing to fill, we're done
if ((w < 1) || (h < 1) || (x1 > target->w) || (y1 > target->h))
return true;
// clip if rectangles too big
if ((x1 + w) > target->w)
w = target->w - x1;
if ((y1 + h) > target->h)
h = target->h - y1;
if ((w < 1) || (h < 1))
return true;
uint8 *start = target->data + y1 * target->stride + x1;
for (int32 i = 0; i < h; i++, start += target->stride)
memset(start, color, w);
return true;
}
} // namespace M4

View File

@@ -0,0 +1,99 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_BUFF_H
#define M4_GRAPHICS_GR_BUFF_H
#include "m4/m4_types.h"
#include "m4/mem/reloc.h"
namespace M4 {
class GrBuff {
protected:
void alloc_pixmap();
Buffer dummy{};
MemHandle pixmap = nullptr;
public:
int32 w, h, x_off, y_off, pitch, height;
public:
GrBuff(int32 _w, int32 _h);
GrBuff(int32 _w, int32 _h, int32 _x_off, int32 _y_off, int32 _pitch, int32 _height);
virtual ~GrBuff();
/**
* Get buffer pointer
* @remarks Get_buffer will return the whole pixmap, not the
* subrectangle specified via x_off, y_off, w, h, and pitch.
* get_buffer will lock the pixmap, unlock it after use, SVP.
* DO NOT FREE THE RETURNED BUFFER!
*/
Buffer *get_buffer();
uint8 *get_pixmap();
void lock();
void release();
void refresh_video(int32 scrnX, int32 scrnY, int32 x1, int32 y1, int32 x2, int32 y2);
};
int32 gr_buffer_free(Buffer *buf);
byte *gr_buffer_pointer(Buffer *buf, int32 x, int32 y);
const byte *gr_buffer_pointer(const Buffer *buf, int32 x, int32 y);
int32 gr_buffer_init(Buffer *buf, const char *name, int32 w, int32 h);
/**
* Copies a "rectangular" buffer area from "from" to "unto". Size
* of copied rectangle is determined by "size_x, size_y". Upper left
* corner in source buffer is indicated by "from_x, from_y", and
* "unto_x, unto_y" determines upper left corner in destination
* buffer (if upper left corner coordinates are the same in both
* buffers, buf_rect_copy() can be used instead).
* @returns Returns true if successful.
*/
bool gr_buffer_rect_copy_2(const Buffer *from, Buffer *to, int32 sx, int32 sy,
int32 dx, int32 dy, int32 w, int32 h);
/**
* Copies a "rectangular" buffer area from "from" to "unto". Size
* of copied rectangle is determined by "size_x, size_y". Upper left
* corner in BOTH buffers is indicated by "ul_x, ul_y". (To copy
* using separate corner coordinates in each buffer, use
* buf_rect_copy_2 ().
* @returns Returns true if successful.
*/
bool gr_buffer_rect_copy(Buffer *from, Buffer *to, int32 x, int32 y, int32 w, int32 h);
/**
* Fills a rectangular buffer area with the specified byte value.
* Upper left corner is determined by "ul_x, ul_y", and sizes are
* determined by "size_x, size_y."
* @returns Returns true if successful; false if buffer invalid.
*/
int32 gr_buffer_rect_fill(Buffer *target, int32 x1, int32 y1, int32 w, int32 h);
void GrBuff_Show(void *s, void *r, void *b, int32 destX, int32 destY);
} // namespace M4
#endif

View File

@@ -0,0 +1,62 @@
/* 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 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/>.
*
*/
#include "m4/graphics/gr_color.h"
#include "m4/core/errors.h"
#include "m4/fileio/sys_file.h"
#include "m4/mem/reloc.h"
namespace M4 {
InvPal::InvPal(const char *filename) {
handle = nullptr;
if (!filename)
return;
SysFile ipl5(filename);
if (!ipl5.exists())
return;
handle = NewHandle(32768, "5 bit ict");
ipl5.read(handle, 32768);
}
InvPal::~InvPal() {
if (handle)
DisposeHandle(handle);
}
uint8 *InvPal::get_ptr() {
if (!handle)
return nullptr;
HLock(handle);
return (uint8 *)*handle;
}
void InvPal::release() {
if (handle)
HUnLock(handle);
}
} // namespace M4

View File

@@ -0,0 +1,44 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_COLOR_H
#define M4_GRAPHICS_GR_COLOR_H
#include "m4/m4_types.h"
#include "m4/mem/reloc.h"
namespace M4 {
class InvPal {
private:
MemHandle handle;
public:
InvPal(const char *filename);
virtual ~InvPal();
uint8 *get_ptr();
void release();
};
} // namespace M4
#endif

View File

@@ -0,0 +1,61 @@
/* 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 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/>.
*
*/
#include "m4/graphics/gr_draw.h"
#include "m4/graphics/gr_line.h"
#include "m4/graphics/gr_pal.h"
namespace M4 {
void buffer_put_pixel(Buffer *buf, int32 x, int32 y, byte c) {
*(buf->data + x + (y * buf->stride)) = (uint8)c;
}
byte buffer_get_pixel(Buffer *buf, int32 x, int32 y) {
return *(buf->data + x + (y * buf->stride));
}
void buffer_draw_box(Buffer *buf, int32 x1, int32 y1, int32 x2, int32 y2, byte color) {
gr_color_set(color);
gr_hline(buf, x1, x2, y1);
gr_hline(buf, x1, x2, y2);
gr_vline(buf, x1, y1, y2);
gr_vline(buf, x2, y1, y2);
}
void buffer_draw_box_xor(Buffer *buf, int32 x1, int32 y1, int32 x2, int32 y2) {
gr_hline_xor(buf, x1, x2, y1);
gr_hline_xor(buf, x1, x2, y2);
gr_vline_xor(buf, x1, y1 + 1, y2 - 1);
gr_vline_xor(buf, x2, y1 + 1, y2 - 1);
}
int32 buffer_fill(Buffer *target, byte value) {
if (!target)
return false;
if (!target->data)
return false;
memset(target->data, value, target->stride * target->h);
return true;
}
} // namespace M4

View File

@@ -0,0 +1,57 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_DRAW_H
#define M4_GRAPHICS_GR_DRAW_H
#include "m4/m4_types.h"
namespace M4 {
/**
* Given X, Y sets the value of the pixel at that position
*/
void buffer_put_pixel(Buffer *buf, int32 x, int32 y, byte c);
/**
* Given X, Y returns value of pixel at that address on the screen
*/
byte buffer_get_pixel(Buffer *buf, int32 x, int32 y);
/**
* Draws outside edge of retangle given home and size along both axis
*/
void buffer_draw_box(Buffer *buf, int32 x1, int32 y1, int32 x2, int32 y2, byte color);
/**
* Draws a rectangle using xor
*/
void buffer_draw_box_xor(Buffer *buf, int32 x1, int32 y1, int32 x2, int32 y2);
/**
* Fills an entire buffer with a single byte value.
* @returns Returns True if successful, FALSE if buffer invalid.
*/
int32 buffer_fill(Buffer *target, byte value);
} // namespace M4
#endif

View File

@@ -0,0 +1,415 @@
/* 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 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/>.
*
*/
#include "m4/graphics/gr_font.h"
#include "m4/graphics/gr_buff.h"
#include "m4/mem/mem.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/mem/memman.h"
#include "m4/vars.h"
namespace M4 {
#define font_width 2 /* offset to width array */
#define font_data 130 /* offset to data array */
#define STR_FONTSTRUCT "font struct"
#define STR_FONTWIDTH "font widths"
#define STR_FONTOFF "font offsets"
#define STR_FONTDATA "font data"
static byte font_colors[4] = { 0, 15, 0, 8 };
constexpr byte font_intr_w = 8;
constexpr byte font_intr_h = 8;
static byte fontintr_width[128] = {
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, // 00-0f width (128 entries)
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,4, // 10-1f
2,3,4,5,4,6,6,3, 3,3,6,3,3,3,3,6, // 20-2f [ !"#]$%&' ()*+,-./
6,6,6,6,6,6,6,6, 6,6,2,2,3,3,3,6, // 30-3f [01234567] [89:;]<=>?
6,6,6,5,6,5,5,6, 6,3,5,5,6,7,6,6, // 40-4f [@ABCDEFG] [HIJKLMNO]
6,6,6,6,6,6,6,6, 6,6,6,3,3,3,4,6, // 50-5f [PQRSTUVW] [XYZ[\]^_]
5,5,5,5,5,5,5,5, 5,2,3,5,2,6,5,5, // 60-6f ['abcdefg] [hijklmno]
5,5,3,5,3,5,5,6, 5,5,5,4,2,4,4,4, // 70-7f [pqrstuvw] []xyz{|}~^
};
static short fontintr_offsets[128] = {
784, 784, 784, 784, 784, 784, 784, 784, // 00-07
784, 784, 784, 784, 784, 784, 784, 784, // 08-0f
784, 784, 784, 784, 784, 784, 784, 784, // 10-17
784, 784, 784, 784, 784, 784, 784, 784, // 18-1f
0, 8, 16, 24, 40, 48, 64, 80, // 20-27 [ !"#$%&']
88, 96, 104, 120, 128, 136, 144, 152, // 28-2f [()*+,-./]
168, 1176, 192, 208, 224, 240, 256, 272, // 30-37 [01234567]
288, 304, 320, 328, 88, 336, 96, 344, // 38-3f [89:;]<=>?
360, 376, 392, 408, 424, 440, 456, 472, // 40-47 [@ABCDEFG]
488, 504, 512, 528, 544, 560, 576, 592, // 48-4f [HIJKLMNO]
608, 624, 640, 656, 672, 688, 704, 720, // 50-57 [PQRSTUVW]
736, 752, 768, 1192, 1208, 1200, 784, 784, // 58-5f [XYZ[\]^_]
784, 800, 816, 832, 848, 864, 880, 896, // 60-67 [`abcdefg]
912, 928, 936, 944, 960, 968, 984, 1000, // 68-6f [hijklmno]
1016, 1032, 1048, 1056, 1072, 1080, 1096, 1112, // 70-77 [pqrstuvw]
1128, 1144, 1160, 784, 184, 1216, 784, 1224 // 78-7f []xyz{|}~^
};
static byte fontintr_data[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0 " "
0x14,0x14,0x14,0x14,0x14,0x00,0x14,0x00, // 8 "!"
0x44,0x44,0x00,0x00,0x00,0x00,0x00,0x00, // 16 "
0x00,0x00,0x11,0x00,0x55,0x40,0x11,0x00,0x11,0x00,0x55,0x40,0x11,0x00,0x00,0x00, // 24 "#"
0x10,0x14,0x40,0x54,0x04,0x50,0x10,0x00, // 40 "$"
0x00,0x00,0x10,0x00,0x44,0x40,0x11,0x00,0x01,0x10,0x04,0x44,0x00,0x10,0x00,0x00, // 48 "%"
0x04,0x00,0x11,0x00,0x10,0x00,0x44,0x00,0x41,0x40,0x41,0x00,0x14,0x40,0x00,0x00, // 64 "&"
0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00, // 80 "'"
0x04,0x10,0x40,0x40,0x40,0x10,0x04,0x00, // 88 "("
0x40,0x10,0x04,0x04,0x04,0x10,0x40,0x00, // 96 ")"
0x00,0x00,0x04,0x00,0x44,0x40,0x15,0x00,0x44,0x40,0x04,0x00,0x00,0x00,0x00,0x00, // 104 "*"
0x00,0x00,0x00,0x10,0x54,0x10,0x00,0x00, // 120 "+"
0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x40, // 128 ","
0x00,0x00,0x00,0x00,0x54,0x00,0x00,0x00, // 136 "-"
0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00, // 144 "."
0x00,0x04,0x00,0x10,0x00,0x40,0x01,0x00,0x04,0x00,0x10,0x00,0x40,0x00,0x00,0x00, // 152 "/"
0x00,0x00,0x15,0x40,0x50,0x10,0x54,0x10,0x51,0x10,0x50,0x50,0x15,0x40,0x00,0x00, // 168 "0"
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, // 184 "|"
0x00,0x00,0x15,0x00,0x41,0x40,0x01,0x40,0x05,0x00,0x14,0x00,0x55,0x40,0x00,0x00, // 192 "2"
0x00,0x00,0x15,0x40,0x00,0x50,0x05,0x40,0x00,0x50,0x00,0x50,0x55,0x40,0x00,0x00, // 208 "3"
0x00,0x00,0x01,0x40,0x41,0x40,0x41,0x40,0x55,0x40,0x01,0x40,0x01,0x40,0x00,0x00, // 224 "4"
0x00,0x00,0x55,0x50,0x50,0x00,0x55,0x40,0x00,0x50,0x00,0x50,0x55,0x40,0x00,0x00, // 240 "5"
0x00,0x00,0x15,0x40,0x50,0x00,0x55,0x40,0x50,0x10,0x50,0x10,0x15,0x40,0x00,0x00, // 256 "6"
0x00,0x00,0x55,0x50,0x00,0x50,0x01,0x40,0x05,0x00,0x05,0x00,0x05,0x00,0x00,0x00, // 272 "7"
0x00,0x00,0x15,0x40,0x50,0x10,0x15,0x40,0x50,0x10,0x50,0x10,0x15,0x40,0x00,0x00, // 288 "8"
0x00,0x00,0x15,0x40,0x40,0x50,0x41,0x50,0x14,0x50,0x00,0x50,0x15,0x40,0x00,0x00, // 304 "9"
0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00, // 320 ":"
0x00,0x00,0x40,0x00,0x00,0x40,0x40,0x00, // 328 ";"
0x00,0x00,0x00,0x54,0x00,0x54,0x00,0x00, // 336 "="
0x14,0x00,0x41,0x00,0x01,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, // 344 "?" // 352
0x15,0x00,0x40,0x40,0x41,0x00,0x44,0x40,0x41,0x00,0x40,0x00,0x15,0x00,0x00,0x00, // 360 "@"
0x15,0x40,0x50,0x10,0x50,0x10,0x55,0x50,0x50,0x10,0x50,0x10,0x50,0x10,0x00,0x00, // 376 "A"
0x55,0x40,0x50,0x10,0x50,0x10,0x55,0x40,0x50,0x10,0x50,0x10,0x55,0x40,0x00,0x00, // 392 "B"
0x15,0x00,0x50,0x40,0x50,0x00,0x50,0x00,0x50,0x00,0x50,0x40,0x15,0x00,0x00,0x00, // 408 "C"
0x55,0x40,0x14,0x40,0x14,0x10,0x14,0x10,0x14,0x10,0x14,0x10,0x55,0x40,0x00,0x00, // 424 "D"
0x55,0x40,0x50,0x00,0x50,0x00,0x55,0x00,0x50,0x00,0x50,0x00,0x55,0x40,0x00,0x00, // 440 "E"
0x55,0x40,0x50,0x00,0x50,0x00,0x55,0x00,0x50,0x00,0x50,0x00,0x50,0x00,0x00,0x00, // 456 "F"
0x15,0x40,0x50,0x10,0x50,0x00,0x51,0x50,0x50,0x10,0x50,0x10,0x15,0x40,0x00,0x00, // 472 "G"
0x50,0x10,0x50,0x10,0x50,0x10,0x55,0x50,0x50,0x10,0x50,0x10,0x50,0x10,0x00,0x00, // 488 "H"
0x55,0x14,0x14,0x14,0x14,0x14,0x55,0x00, // 504 "I"
0x05,0x50,0x01,0x40,0x01,0x40,0x01,0x40,0x01,0x40,0x41,0x40,0x15,0x00,0x00,0x00, // 512 "J"
0x50,0x40,0x51,0x00,0x54,0x00,0x50,0x00,0x54,0x00,0x51,0x00,0x50,0x40,0x00,0x00, // 528 "K"
0x50,0x00,0x50,0x00,0x50,0x00,0x50,0x00,0x50,0x00,0x50,0x00,0x55,0x50,0x00,0x00, // 544 "L"
0x50,0x10,0x54,0x50,0x51,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x00,0x00, // 560 "M"
0x50,0x10,0x50,0x10,0x54,0x10,0x51,0x10,0x50,0x50,0x50,0x10,0x50,0x10,0x00,0x00, // 576 "N"
0x15,0x40,0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x15,0x40,0x00,0x00, // 592 "O"
0x55,0x00,0x50,0x40,0x50,0x40,0x55,0x00,0x50,0x00,0x50,0x00,0x50,0x00,0x00,0x00, // 608 "P"
0x15,0x00,0x50,0x40,0x50,0x40,0x50,0x40,0x50,0x40,0x51,0x00,0x14,0x40,0x00,0x00, // 624 "Q"
0x55,0x40,0x50,0x10,0x50,0x10,0x55,0x40,0x50,0x10,0x50,0x10,0x50,0x10,0x00,0x00, // 640 "R"
0x15,0x00,0x40,0x40,0x10,0x00,0x05,0x40,0x00,0x50,0x40,0x50,0x15,0x40,0x00,0x00, // 656 "S"
0x55,0x50,0x05,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x00,0x00, // 672 "T"
0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x15,0x40,0x00,0x00, // 688 "U"
0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x10,0x40,0x04,0x00,0x00,0x00, // 704 "V"
0x50,0x10,0x50,0x10,0x50,0x10,0x50,0x10,0x51,0x10,0x55,0x50,0x50,0x10,0x00,0x00, // 720 "W"
0x40,0x40,0x40,0x40,0x11,0x00,0x04,0x00,0x11,0x00,0x40,0x40,0x40,0x40,0x00,0x00, // 736 "X"
0x40,0x40,0x40,0x40,0x11,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00, // 752 "Y"
0x55,0x40,0x00,0x40,0x01,0x00,0x04,0x00,0x10,0x00,0x40,0x00,0x55,0x40,0x00,0x00, // 768 "Z"
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x40,0x00,0x00, // 784 a little weirdo thing
0x00,0x00,0x00,0x00,0x15,0x00,0x01,0x40,0x11,0x40,0x41,0x40,0x14,0x40,0x00,0x00, // 800 "a"
0x50,0x00,0x50,0x00,0x55,0x00,0x50,0x40,0x50,0x40,0x50,0x40,0x55,0x00,0x00,0x00, // 816 "b"
0x00,0x00,0x00,0x00,0x15,0x00,0x50,0x40,0x50,0x00,0x50,0x40,0x15,0x00,0x00,0x00, // 832 "c"
0x01,0x40,0x01,0x40,0x15,0x40,0x41,0x40,0x41,0x40,0x41,0x40,0x15,0x40,0x00,0x00, // 848 "d"
0x00,0x00,0x00,0x00,0x15,0x00,0x50,0x40,0x55,0x40,0x50,0x00,0x15,0x00,0x00,0x00, // 864 "e"
0x05,0x00,0x11,0x00,0x14,0x00,0x55,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x00,0x00, // 880 "f"
0x00,0x00,0x00,0x00,0x14,0x00,0x41,0x40,0x41,0x40,0x15,0x40,0x41,0x40,0x15,0x00, // 896 "g"
0x50,0x00,0x50,0x00,0x55,0x00,0x50,0x40,0x50,0x40,0x50,0x40,0x50,0x40,0x00,0x00, // 912 "h"
0x50,0x00,0x50,0x50,0x50,0x50,0x50,0x00, // 928 "i"
0x05,0x00,0x05,0x05,0x05,0x05,0x45,0x10, // 936 "j"
0x50,0x00,0x50,0x00,0x50,0x40,0x51,0x00,0x54,0x00,0x51,0x00,0x50,0x40,0x00,0x00, // 944 "k"
0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x00, // 960 "l"
0x00,0x00,0x00,0x00,0x54,0x40,0x51,0x10,0x51,0x10,0x51,0x10,0x51,0x10,0x00,0x00, // 968 "m"
0x00,0x00,0x00,0x00,0x55,0x00,0x50,0x40,0x50,0x40,0x50,0x40,0x50,0x40,0x00,0x00, // 984 "n"
0x00,0x00,0x00,0x00,0x15,0x00,0x50,0x40,0x50,0x40,0x50,0x40,0x15,0x00,0x00,0x00, // 1000 "o"
0x00,0x00,0x00,0x00,0x55,0x00,0x50,0x40,0x50,0x40,0x55,0x00,0x50,0x00,0x50,0x00, // 1016 "p"
0x00,0x00,0x00,0x00,0x14,0x40,0x41,0x40,0x41,0x40,0x15,0x40,0x01,0x40,0x01,0x40, // 1032 "q"
0x00,0x00,0x51,0x54,0x50,0x50,0x50,0x00, // 1048 "r"
0x00,0x00,0x00,0x00,0x15,0x00,0x40,0x00,0x15,0x00,0x01,0x40,0x55,0x00,0x00,0x00, // 1056 "s"
0x10,0x14,0x55,0x14,0x14,0x14,0x05,0x00, // 1072 "t"
0x00,0x00,0x00,0x00,0x50,0x40,0x50,0x40,0x50,0x40,0x50,0x40,0x15,0x00,0x00,0x00, // 1080 "u"
0x00,0x00,0x00,0x00,0x50,0x40,0x50,0x40,0x50,0x40,0x11,0x10,0x05,0x00,0x00,0x00, // 1096 "v"
0x00,0x00,0x00,0x00,0x50,0x10,0x50,0x10,0x51,0x10,0x51,0x10,0x15,0x00,0x00,0x00, // 1112 "w"
0x00,0x00,0x00,0x00,0x50,0x40,0x50,0x40,0x15,0x00,0x50,0x40,0x50,0x40,0x00,0x00, // 1128 "x"
0x00,0x00,0x00,0x00,0x41,0x40,0x41,0x40,0x41,0x40,0x15,0x40,0x01,0x40,0x01,0x40, // 1144 "y
0x00,0x00,0x00,0x00,0x55,0x00,0x01,0x00,0x14,0x00,0x40,0x00,0x55,0x00,0x00,0x00, // 1160 "z"
0x00,0x00,0x05,0x00,0x15,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x15,0x40,0x00,0x00, // 1176 thick "1"
0x55,0x50,0x50,0x50,0x50,0x50,0x55,0x00, // 1192 "["
0x55,0x05,0x05,0x05,0x05,0x05,0x55,0x00, // 1200 "]"
0x00,0x40,0x10,0x10,0x04,0x04,0x01,0x00, // 1208 "\"
0x15,0x14,0x14,0x50,0x14,0x14,0x15,0x00, // 1216 "{"
0x54,0x14,0x14,0x05,0x14,0x14,0x54,0x00 // 1224 "}"
};
void font_set_colors(uint8 alt1, uint8 alt2, uint8 foreground) {
if (_G(font) == _G(interfaceFont)) {
font_colors[1] = foreground;
} else {
font_colors[1] = alt1;
font_colors[2] = alt2;
font_colors[3] = foreground;
}
}
void gr_font_set_color(uint8 foreground) {
if (_G(font) == _G(interfaceFont))
font_colors[1] = foreground;
else
font_colors[3] = foreground;
}
Font *gr_font_create_system_font() {
_G(interfaceFont) = (Font *)mem_alloc(sizeof(Font), "Font");
if (!_G(interfaceFont))
error("font struct");
_G(interfaceFont)->max_y_size = font_intr_h;
_G(interfaceFont)->max_x_size = font_intr_w;
_G(interfaceFont)->width = fontintr_width;
_G(interfaceFont)->offset = fontintr_offsets;
_G(interfaceFont)->pixData = fontintr_data;
return _G(interfaceFont);
}
void gr_font_system_shutdown() {
if (_G(interfaceFont))
mem_free(_G(interfaceFont));
}
void gr_font_dealloc(Font *killMe) {
if (!killMe)
return;
if (killMe->width)
mem_free(killMe->width);
if (killMe->offset)
mem_free(killMe->offset);
if (killMe->pixData)
mem_free(killMe->pixData);
mem_free(killMe);
}
void gr_font_set(Font *newFont) {
if (newFont)
_G(font) = newFont;
}
Font *gr_font_get() {
return _G(font);
}
int32 gr_font_string_width(char *out_string, int32 auto_spacing) {
if (_G(custom_ascii_converter)) { // if there is a function to convert the extended ASCII characters
_G(custom_ascii_converter)(out_string); // call it with the string
}
int32 width = 0; // Add some spacing in between the characters
byte *widthArray = _G(font)->width;
while (*out_string) {
width += widthArray[(byte)*out_string] + auto_spacing;
out_string++;
}
return width;
}
int32 gr_font_string_width(const Common::String &str, int32 auto_spacing) {
char *tmp = new char[str.size() + 1];
Common::copy(str.c_str(), str.c_str() + str.size() + 1, tmp);
const int32 result = gr_font_string_width(tmp, auto_spacing);
delete[] tmp;
return result;
}
int32 gr_font_get_height() {
if (!_G(font))
return -1;
return (int32)_G(font)->max_y_size;
}
int32 gr_font_write(Buffer *target, char *out_string, int32 x, int32 y, int32 w, int32 auto_spacing) {
if (!target || !out_string)
return x;
if (_G(custom_ascii_converter)) { // if there is a function to convert the extended ASCII characters
_G(custom_ascii_converter)(out_string); // call it with the string
}
int32 target_w;
if (w)
target_w = imath_min(target->w, x + w);
else
target_w = target->w;
x += 1;
y += 1;
int32 skipTop = 0;
if (y < 0) {
skipTop = -y;
y = 0;
}
int32 height = imath_max(0, (int32)_G(font)->max_y_size - skipTop);
if (!height)
return x;
const int32 bottom = y + height - 1;
if (bottom > (target->h - 1)) {
height -= imath_min((int32)height, (bottom - (target->h - 1)));
}
if (height <= 0)
return x;
byte *target_ptr = gr_buffer_pointer(target, x, y);
byte *prev_target_ptr = target_ptr;
int32 cursX = x;
Byte *widthArray = _G(font)->width;
Byte *fontPixData = _G(font)->pixData;
short *offsetArray = _G(font)->offset;
while (*out_string) {
const byte c = (*out_string++) & 0x7f;
const int32 wdth = widthArray[c];
// if width is zero, nothing to draw
if (wdth) {
if ((cursX + wdth) >= target_w) // if character doesn't fit in buffer, abort
return cursX;
const int32 offset = offsetArray[c];
Byte *charData = &fontPixData[offset];
const int32 bytesInChar = (_G(font)->width[c] >> 2) + 1; // bytesPer[wdth]; // 2 bits per pixel
if (skipTop)
charData += bytesInChar * skipTop;
for (int32 i = 0; i < height; i++) {
for (int32 j = 0; j < bytesInChar; j++) {
const Byte workByte = *charData++;
if (workByte & 0xc0)
*target_ptr = font_colors[(workByte & 0xc0) >> 6];
target_ptr++;
if (workByte & 0x30)
*target_ptr = font_colors[(workByte & 0x30) >> 4];
target_ptr++;
if (workByte & 0xc)
*target_ptr = font_colors[(workByte & 0xc) >> 2];
target_ptr++;
if (workByte & 0x3)
*target_ptr = font_colors[workByte & 0x3];
target_ptr++;
} // end bytes per character line loop
target_ptr += target->stride - (bytesInChar << 2);
} // end for height loop
target_ptr = prev_target_ptr + wdth + auto_spacing; // one pixel space
prev_target_ptr = target_ptr;
} // end if there was a drawable character
cursX += w;
} // end while there is a character to draw loop
return cursX;
}
int32 gr_font_write(Buffer *target, const char *out_string, int32 x, int32 y, int32 w, int32 auto_spacing) {
char *tmp = mem_strdup(out_string);
const int32 result = gr_font_write(target, tmp, x, y, w, auto_spacing);
free(tmp);
return result;
}
Font *gr_font_load(const char *fontName) {
SysFile fontFile(fontName);
if (!fontFile.exists())
return nullptr;
uint32 tag = fontFile.readUint32LE();
if (tag != 'FONT')
error_show(FL, 'FNTL', "font: %s chkpnt: %d", (const char *)fontName, 0);
Font *newFont = (Font *)mem_alloc(sizeof(Font), STR_FONTSTRUCT);
if (!newFont)
error_show(FL, 'OOM!', "_G(font) struct");
newFont->max_y_size = fontFile.readByte();
newFont->max_x_size = fontFile.readByte();
newFont->dataSize = fontFile.readUint32LE();
// read 'WIDT' into tag
tag = fontFile.readUint32LE();
if (tag != 'WIDT')
error_show(FL, 'FNTL', "font: %s chkpnt: %d", fontName, 1);
// width table
newFont->width = (byte *)mem_alloc(256, STR_FONTWIDTH);
if (!newFont->width)
error_show(FL, 'OOM!', "_G(font) width table");
fontFile.read(newFont->width, 256);
// read 'OFFS' into tag
tag = fontFile.readUint32LE();
if (tag != 'OFFS')
error_show(FL, 'FNTL', "font: %s chkpnt: %d", fontName, 2);
// offset table
newFont->offset = (short *)mem_alloc(256 * sizeof(int16), STR_FONTOFF);
if (!newFont->offset)
error_show(FL, 'OOM!', "font offset table");
for (int i = 0; i < 256; i++)
newFont->offset[i] = fontFile.readSint16LE();
// read 'PIXS' into tag
tag = fontFile.readUint32LE();
if (tag != 'PIXS')
error_show(FL, 'FNTL', "font: %s chkpnt: %d", fontName, 3);
// pixData
newFont->pixData = (byte *)mem_alloc(newFont->dataSize, STR_FONTDATA);
if (!newFont->pixData)
error_show(FL, 'OOM!', "font pix data");
fontFile.read(newFont->pixData, newFont->dataSize);
// we don't need to close the file, because the destructor will close fontFile automagically
return newFont;
}
} // namespace M4

View File

@@ -0,0 +1,60 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_FONT_H
#define M4_GRAPHICS_GR_FONT_H
#include "common/str.h"
#include "m4/m4_types.h"
namespace M4 {
struct Font {
byte max_y_size;
byte max_x_size;
uint32 dataSize;
byte *width;
int16 *offset;
byte *pixData;
};
void gr_font_system_shutdown();
void gr_font_dealloc(Font *killMe);
Font *gr_font_create_system_font();
void gr_font_set_color(uint8 foreground);
Font *gr_font_get();
void gr_font_set(Font *font);
int32 gr_font_get_height();
int32 gr_font_write(Buffer *target, char *out_string, int32 x, int32 y,
int32 w, int32 auto_spacing = 1);
int32 gr_font_write(Buffer *target, const char *out_string, int32 x, int32 y,
int32 w, int32 auto_spacing);
int32 gr_font_string_width(char *out_string, int32 auto_spacing = 1);
int32 gr_font_string_width(const Common::String &str, int32 auto_spacing = 1);
Font *gr_font_load(const char *fontName);
void font_set_colors(uint8 alt1, uint8 alt2, uint8 foreground);
} // namespace M4
#endif

View File

@@ -0,0 +1,138 @@
/* 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 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/>.
*
*/
#include "m4/graphics/gr_line.h"
#include "m4/vars.h"
namespace M4 {
void gr_vline_xor(Buffer *buf, int32 x, int32 y1, int32 y2) {
if (y1 > y2) {
SWAP(y1, y2);
}
if ((x > buf->w) || (y1 > buf->h))
return;
if (y2 > buf->h)
y2 = buf->h; // Don't draw past bottom
byte *start = buf->data + x;
for (int32 i = y1; i < y2; i++, start += buf->stride)
*start ^= 0xff;
}
void gr_hline_xor(Buffer *buf, int32 x1, int32 x2, int32 y) {
if (x1 > x2) {
SWAP(x1, x2);
}
if ((y > buf->h) || (x1 > buf->w))
return;
byte *start = gr_buffer_pointer(buf, x1, y);
for (int32 i = x1; i < x2; i++, start++)
*start ^= 0xff;
}
void gr_vline(Buffer *buf, int32 x, int32 y1, int32 y2) {
if (y1 > y2) {
SWAP(y1, y2);
}
if ((x > buf->w) || (y1 > buf->h))
return;
y2++;
if (y2 > buf->h)
y2 = buf->h; // don't draw past bottom
byte *start = gr_buffer_pointer(buf, x, y1);
for (int32 i = y1; i < y2; i++, start += buf->stride)
*start = _G(color);
}
void gr_hline(Buffer *buf, int32 x1, int32 x2, int32 y) {
if (x1 > x2) {
SWAP(x1, x2);
}
if ((y > buf->h) || (x1 > buf->w))
return;
byte *start = gr_buffer_pointer(buf, x1, y);
x2++;
if (x2 > buf->w)
x2 = buf->w;
for (int32 i = x1; i < x2; i++, start++)
*start = _G(color);
}
void gr_line(int32 x1, int32 y1, int32 x2, int32 y2, int32 color, Buffer *screen) {
byte *myData = (byte *)screen->data;
int32 y_unit, x_unit; // Variables for amount of change in x and y
int32 offset = y1 * screen->stride + x1; // Calculate offset into video RAM
int32 ydiff = y2 - y1; // Calculate difference between y coordinates
if (ydiff < 0) { // If the line moves in the negative direction
ydiff = -ydiff; // ...get absolute value of difference
y_unit = -screen->stride; // ...and set negative unit in y dimension
} else y_unit = screen->stride; // Else set positive unit in y dimension
int32 xdiff = x2 - x1; // Calculate difference between x coordinates
if (xdiff < 0) { // If the line moves in the negative direction
xdiff = -xdiff; // ...get absolute value of difference
x_unit = -1; // ...and set negative unit in x dimension
} else x_unit = 1; // Else set positive unit in y dimension
int32 error_term = 0; // Initialize error term
if (xdiff > ydiff) { // If difference is bigger in x dimension
const int32 length = xdiff + 1; // ...prepare to count off in x direction
for (int32 i = 0; i < length; i++) { // Loop through points in x direction
myData[offset] = (char)color; // Set the next pixel in the line to COLOR
offset += x_unit; // Move offset to next pixel in x direction
error_term += ydiff; // Check to see if move required in y direction
if (error_term > xdiff) { // If so...
error_term -= xdiff; // ...reset error term
offset += y_unit; // ...and move offset to next pixel in y dir.
}
}
} else { // If difference is bigger in y dimension
const int32 length = ydiff + 1; // ...prepare to count off in y direction
for (int32 i = 0; i < length; i++) { // Loop through points in y direction
myData[offset] = (char)color; // Set the next pixel in the line to COLOR
offset += y_unit; // Move offset to next pixel in y direction
error_term += xdiff; // Check to see if move required in x direction
if (error_term > 0) { // If so...
error_term -= ydiff; // ...reset error term
offset += x_unit; // ...and move offset to next pixel in x dir.
}
}
}
}
} // namespace M4

View File

@@ -0,0 +1,46 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_LINE_H
#define M4_GRAPHICS_GR_LINE_H
#include "m4/m4_types.h"
namespace M4 {
/**
* Given starting and ending points on the Y axis, and the constant
* X value, draws a line in the set color in the specified buffer
*/
void gr_hline(Buffer *buf, int32 x1, int32 x2, int32 y);
/**
* Given starting and ending points on the Y axis, and the constant
* X value, draws a line in the given color on the live MCGA screen.
*/
void gr_vline(Buffer *buf, int32 x, int32 y1, int32 y2);
void gr_hline_xor(Buffer *buf, int32 x1, int32 x2, int32 y);
void gr_vline_xor(Buffer *buf, int32 x, int32 y1, int32 y2);
void gr_line(int32 x1, int32 y1, int32 x2, int32 y2, int32 color, Buffer *screen);
} // namespace M4
#endif

View File

@@ -0,0 +1,188 @@
/* 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 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/>.
*
*/
#include "common/system.h"
#include "graphics/paletteman.h"
#include "common/textconsole.h"
#include "m4/graphics/gr_pal.h"
#include "m4/core/errors.h"
#include "m4/vars.h"
namespace M4 {
byte EGAcolors[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
uint8 gr_pal_get_ega_color(uint8 myColor) {
return EGAcolors[myColor];
}
uint8 *gr_color_createInverseTable(RGB8 *pal, uint8 bitDepth, int begin_color, int end_color) {
error("gr_color_createInverseTable is not implemented in ScummVM");
}
void gr_color_create_ipl5(uint8 *inverseColorTable, char *fname, int room_num) {
error("gr_color_create_ipl5 is not implemented in ScummVM");
}
uint8 *gr_color_load_ipl5(const char *filename, uint8 *inverseColors) {
error("gr_color_load_ipl5 is not implemented in ScummVM");
}
void gr_color_set(int32 c) {
_G(color) = c;
}
byte gr_color_get_current() {
return _G(color);
}
void gr_pal_clear(RGB8 *palette) {
for (int i = 0; i < 256; i++) {
palette[i].r = 0;
palette[i].g = 0;
palette[i].b = 0;
}
gr_pal_set(palette);
}
void gr_pal_set(RGB8 *pal) {
gr_pal_set_range(pal, 0, 256);
}
void gr_pal_set_RGB8(RGB8 *entry, int r, int g, int b) {
entry->r = (byte)r;
entry->g = (byte)g;
entry->b = (byte)b;
}
void gr_pal_set_range(RGB8 *pal, int first_color, int num_colors) {
g_system->getPaletteManager()->setPalette((const byte *)pal + first_color * 3,
first_color, num_colors);
}
void gr_pal_set_range(int first_color, int num_colors) {
gr_pal_set_range(_G(master_palette), first_color, num_colors);
}
void gr_pal_set_entry(int32 index, RGB8 *entry) {
g_system->getPaletteManager()->setPalette((const byte *)entry, index, 1);
}
void gr_pal_clear_range(RGB8 *palette, int first_color, int last_color) {
for (int i = first_color; i <= last_color; i++) {
palette[i].r = 0;
palette[i].g = 0;
palette[i].b = 0;
}
gr_pal_set_range(palette, first_color, last_color - first_color);
}
uint8 gr_pal_find_best_match(RGB8 *pal, uint8 r, uint8 g, uint8 b) {
int index = 0;
uint32 minDist = 0x7fffffff;
for (int i = 0; i < 256; ++i) {
const int Rdiff = r - pal[i].r;
const int Gdiff = g - pal[i].g;
const int Bdiff = b - pal[i].b;
if (Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff < (int)minDist) {
minDist = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
index = i;
}
}
return (uint8)index;
}
void gr_pal_interface(RGB8 *fixpal) {
if (_GI().set_interface_palette(fixpal))
return;
// Low intensity
gr_pal_set_RGB8(&fixpal[0], 0, 0, 0); // r0 g0 b0 black
gr_pal_set_RGB8(&fixpal[1], 0, 0, 168); // r0 g0 b2 blue
gr_pal_set_RGB8(&fixpal[2], 0, 168, 0); // r0 g2 b0 green
gr_pal_set_RGB8(&fixpal[3], 0, 168, 168); // r0 g2 b2 cyan
gr_pal_set_RGB8(&fixpal[4], 168, 0, 0); // r2 g0 b0 red
gr_pal_set_RGB8(&fixpal[5], 168, 0, 168); // r2 g0 b2 violet
gr_pal_set_RGB8(&fixpal[6], 168, 92, 0); // r2 g1 b0 brown
gr_pal_set_RGB8(&fixpal[7], 168, 168, 168); // r2 g2 b2 light grey
// high intensity
gr_pal_set_RGB8(&fixpal[8], 92, 92, 92); // r1 g1 b1 dark grey
gr_pal_set_RGB8(&fixpal[9], 92, 92, 255); // r1 g1 b2 light blue
gr_pal_set_RGB8(&fixpal[10], 92, 255, 92); // r1 g2 b1 light green
gr_pal_set_RGB8(&fixpal[11], 92, 255, 255); // r1 g2 b2 light cyan
gr_pal_set_RGB8(&fixpal[12], 255, 92, 92); // r2 g1 b1 light red
gr_pal_set_RGB8(&fixpal[13], 255, 92, 255); // r2 g1 b2 pink
gr_pal_set_RGB8(&fixpal[14], 255, 255, 23); // r2 g2 b1 yellow
gr_pal_set_RGB8(&fixpal[15], 255, 255, 255);// r1 g1 b1 white
}
void gr_pal_reset_ega_colors(RGB8 *pal) {
EGAcolors[0] = gr_pal_find_best_match(pal, 0, 0, 0); //__BLACK
EGAcolors[1] = gr_pal_find_best_match(pal, 0, 0, 255); //__BLUE
EGAcolors[2] = gr_pal_find_best_match(pal, 0, 255, 0); //__GREEN
EGAcolors[3] = gr_pal_find_best_match(pal, 0, 255, 255); //__CYAN
EGAcolors[4] = gr_pal_find_best_match(pal, 255, 0, 0); //__RED
EGAcolors[5] = gr_pal_find_best_match(pal, 255, 0, 255); //__VIOLET
EGAcolors[6] = gr_pal_find_best_match(pal, 168, 84, 84); //__BROWN
EGAcolors[7] = gr_pal_find_best_match(pal, 168, 168, 168); //__LTGRAY
EGAcolors[8] = gr_pal_find_best_match(pal, 84, 84, 84); //__DKGRAY
EGAcolors[9] = gr_pal_find_best_match(pal, 0, 0, 127); //__LTBLUE
EGAcolors[10] = gr_pal_find_best_match(pal, 0, 127, 0); //__LTGREEN
EGAcolors[11] = gr_pal_find_best_match(pal, 0, 127, 127); //__LTCYAN
EGAcolors[12] = gr_pal_find_best_match(pal, 84, 0, 0); //__LTRED
EGAcolors[13] = gr_pal_find_best_match(pal, 84, 0, 0); //__PINK
EGAcolors[14] = gr_pal_find_best_match(pal, 0, 84, 84); //__YELLOW
EGAcolors[15] = gr_pal_find_best_match(pal, 255, 255, 255); //__WHITE
}
void gr_backup_palette() {
Common::copy(_G(master_palette), _G(master_palette) + 256, _G(backup_palette));
}
void gr_restore_palette() {
Common::copy(_G(backup_palette), _G(backup_palette) + 256, _G(master_palette));
}
void pal_mirror_colours(int first_color, int last_color, RGB8 *pal) {
if (first_color < 0 || last_color > 255 || first_color > last_color)
error_show(FL, 'Burg', "pal_mirror_colours index error");
const int num_colors = last_color - first_color + 1;
for (int index = 0; index < num_colors; ++index) {
RGB8 *destP = pal + (last_color + num_colors - index);
RGB8 *srcP = pal + (first_color + index);
*destP = *srcP;
}
}
void pal_mirror_colours(int first_color, int last_color) {
pal_mirror_colours(first_color, last_color, _G(master_palette));
}
} // namespace M4

View File

@@ -0,0 +1,74 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_PAL_H
#define M4_GRAPHICS_GR_PAL_H
#include "m4/m4_types.h"
namespace M4 {
constexpr int MIN_PAL_ENTRY = 1;
constexpr int MAX_PAL_ENTRY = 255;
#define __BLACK (gr_pal_get_ega_color(0))
#define __BLUE (gr_pal_get_ega_color(1))
#define __GREEN (gr_pal_get_ega_color(2))
#define __CYAN (gr_pal_get_ega_color(3))
#define __RED (gr_pal_get_ega_color(4))
#define __VIOLET (gr_pal_get_ega_color(5))
#define __BROWN (gr_pal_get_ega_color(6))
#define __LTGRAY (gr_pal_get_ega_color(7))
#define __DKGRAY (gr_pal_get_ega_color(8))
#define __LTBLUE (gr_pal_get_ega_color(9))
#define __LTGREEN (gr_pal_get_ega_color(10))
#define __LTCYAN (gr_pal_get_ega_color(11))
#define __LTRED (gr_pal_get_ega_color(12))
#define __PINK (gr_pal_get_ega_color(13))
#define __YELLOW (gr_pal_get_ega_color(14))
#define __WHITE (gr_pal_get_ega_color(15))
uint8 gr_pal_get_ega_color(uint8 myColor);
void gr_color_create_ipl5(uint8 *inverseColorTable, char *fname, int room_num);
uint8 *gr_color_load_ipl5(const char *filename, uint8 *inverseColors);
void gr_color_set(int32 c);
byte gr_color_get_current();
void gr_pal_set_range(RGB8 *pal, int first_color, int num_colors);
void gr_pal_set_range(int first_color, int num_colors);
void gr_pal_set(RGB8 *pal);
void gr_pal_set_RGB8(RGB8 *entry, int r, int g, int b);
void gr_pal_set_entry(int32 index, RGB8 *entry);
void gr_pal_clear(RGB8 *palette);
void gr_pal_clear_range(RGB8 *palette, int first_color, int last_color);
uint8 gr_pal_find_best_match(RGB8 *pal, uint8 r, uint8 g, uint8 b);
void gr_pal_interface(RGB8 *fixpal);
void gr_pal_reset_ega_colors(RGB8 *pal);
void gr_backup_palette();
void gr_restore_palette();
void pal_mirror_colours(int first_color, int last_color, RGB8 *pal);
void pal_mirror_colours(int first_color, int last_color);
} // namespace M4
#endif

View File

@@ -0,0 +1,346 @@
/* 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 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/>.
*
*/
#include "m4/graphics/gr_series.h"
#include "m4/core/errors.h"
#include "m4/wscript/ws_load.h"
#include "m4/wscript/ws_machine.h"
#include "m4/wscript/wst_regs.h"
#include "m4/vars.h"
#include "m4/m4.h"
namespace M4 {
void Series::play(const char *seriesName, frac16 layer, uint32 flags,
int16 triggerNum, int32 frameRate, int32 loopCount, int32 s,
int32 x, int32 y, int32 firstFrame, int32 lastFrame) {
_series = M4::series_play(seriesName, layer, flags, triggerNum, frameRate,
loopCount, s, x, y, firstFrame, lastFrame);
const Common::String shadow = Common::String::format("%ss", seriesName);
_seriesS = M4::series_play(shadow.c_str(), layer + 1, flags, -1, frameRate,
loopCount, s, x, y, firstFrame, lastFrame);
}
void Series::show(const char *seriesName, frac16 layer, uint32 flags,
int16 triggerNum, int32 duration, int32 index, int32 s, int32 x, int32 y) {
_series = M4::series_show(seriesName, layer, flags, triggerNum, duration,
index, s, x, y);
const Common::String shadow = Common::String::format("%ss", seriesName);
_seriesS = M4::series_show(shadow.c_str(), layer + 1, flags, -1, duration,
index, s, x, y);
}
void Series::show(const char *series1, const char *series2, int layer) {
_series = M4::series_show(series1, layer);
_seriesS = M4::series_show(series2, layer + 1);
}
void Series::show_index2(const char *series1, const char *series2, int layer, int index1, int index2) {
_series = M4::series_show(series1, layer, 0, -1, -1, index1);
_seriesS = M4::series_show(series2, layer + 1, 0, -1, -1, index1 + 1);
}
void Series::series_play(const char *seriesName, frac16 layer, uint32 flags,
int16 triggerNum, int32 frameRate, int32 loopCount, int32 s,
int32 x, int32 y, int32 firstFrame, int32 lastFrame) {
Series tmp;
tmp.play(seriesName, layer, flags, triggerNum, frameRate,
loopCount, s, x, y, firstFrame, lastFrame);
}
void Series::series_show(const char *seriesName, frac16 layer, uint32 flags,
int16 triggerNum, int32 duration, int32 index, int32 s, int32 x, int32 y) {
Series tmp;
tmp.show(seriesName, layer, flags, triggerNum, duration,
index, s, x, y);
}
void Series::terminate() {
if (_series)
terminateMachineAndNull(_series);
if (_seriesS)
terminateMachineAndNull(_seriesS);
}
static void series_trigger_dispatch_callback(frac16 myMessage, machine * /*sender*/) {
kernel_trigger_dispatchx(myMessage);
}
int32 series_load(const char *seriesName, int32 assetIndex, RGB8 *myPal) {
const int32 myAssetIndex = AddWSAssetCELS(seriesName, assetIndex, myPal);
if ((myAssetIndex < 0) || (myAssetIndex >= 256))
error_show(FL, 'SPNF', seriesName);
return myAssetIndex;
}
void series_unload(int32 assetIndex) {
ClearWSAssets(_WS_ASSET_CELS, assetIndex, assetIndex);
}
bool series_draw_sprite(int32 spriteHash, int32 index, Buffer *destBuff, int32 x, int32 y) {
M4sprite srcSprite;
M4Rect clipRect, updateRect;
if (!destBuff) {
error_show(FL, 'BUF!');
return false;
}
M4sprite *srcSpritePtr = &srcSprite;
if ((srcSpritePtr = GetWSAssetSprite(nullptr, (uint32)spriteHash, (uint32)index, srcSpritePtr, nullptr)) == nullptr)
error_show(FL, 'SPNF', "hash: %d, index: %d", spriteHash, index);
HLock(srcSpritePtr->sourceHandle);
//gr_pal_interface(&master_palette[0]);
srcSpritePtr->data = (uint8 *)((intptr)*(srcSpritePtr->sourceHandle) + srcSpritePtr->sourceOffset);
RendGrBuff Destination;
DrawRequestX dr;
RendCell Frame;
Destination.Width = destBuff->stride;
Destination.Height = destBuff->h;
Destination.PixMap = (void *)destBuff->data;
dr.x = x;
dr.y = y;
dr.scale_x = 100;
dr.scale_y = 100;
dr.depth_map = destBuff->data;
dr.Pal = nullptr;
dr.ICT = nullptr;
dr.depth = 0;
Frame.hot_x = srcSpritePtr->xOffset;
Frame.hot_y = srcSpritePtr->yOffset;
Frame.Width = srcSpritePtr->w;
Frame.Height = srcSpritePtr->h;
Frame.Comp = (uint32)srcSpritePtr->encoding;
Frame.data = srcSpritePtr->data;
clipRect.x1 = 0;
clipRect.y1 = 0;
clipRect.x2 = Destination.Width;
clipRect.y2 = Destination.Height;
// and draw the sprite
render_sprite_to_8BBM(&Destination, &dr, &Frame, &clipRect, &updateRect);
HUnLock(srcSpritePtr->sourceHandle);
return true;
}
bool series_show_frame(int32 spriteHash, int32 index, Buffer *destBuff, int32 x, int32 y) {
return series_draw_sprite(spriteHash, index, destBuff, x, y);
}
machine *series_stream(const char *seriesName, int32 frameRate, int32 layer, int32 trigger) {
SysFile *sysFile = new SysFile(seriesName);
// Store the frameRate in g_temp1
// If it is < 0, the default frame rate for the ss will be used
_G(globals)[GLB_TEMP_1] = frameRate << 16;
// Store the SysFile pointer
_G(globals)[GLB_TEMP_4] = (intptr)sysFile;
// Set the callback trigger
_G(globals)[GLB_TEMP_5] = kernel_trigger_create(trigger);
// Set the layer
_G(globals)[GLB_TEMP_6] = layer << 16;
machine *m = kernel_spawn_machine(seriesName, HASH_STREAM_MACHINE, series_trigger_dispatch_callback);
return m;
}
bool series_stream_break_on_frame(machine *m, int32 frameNum, int32 trigger) {
// Parameter verification
if (!m)
return false;
_G(globals)[GLB_TEMP_2] = frameNum << 16;
_G(globals)[GLB_TEMP_3] = kernel_trigger_create(trigger);
// Send the message to the machine to accept the new callback frame num and trigger
sendWSMessage(0x10000, 0, m, 0, nullptr, 1);
return true;
}
void series_set_frame_rate(machine *m, int32 newFrameRate) {
if ((!m) || (!m->myAnim8) || !verifyMachineExists(m)) {
if (g_engine->getGameType() == GType_Burger)
error_show(FL, 'SSFR');
return;
}
m->myAnim8->myRegs[IDX_CELS_FRAME_RATE] = newFrameRate << 16;
}
machine *series_show(const char *seriesName, frac16 layer, uint32 flags, int16 triggerNum,
int32 duration, int32 index, int32 s, int32 x, int32 y) {
int32 myAssetIndex;
RGB8 *tempPalettePtr = nullptr;
term_message(seriesName);
if (flags & SERIES_LOAD_PALETTE)
tempPalettePtr = &_G(master_palette)[0];
if ((myAssetIndex = AddWSAssetCELS(seriesName, -1, tempPalettePtr)) < 0)
error_show(FL, 'SPNF', seriesName);
_G(globals)[GLB_TEMP_1] = (frac16)myAssetIndex << 24; // cels hash
_G(globals)[GLB_TEMP_2] = layer << 16; // layer
_G(globals)[GLB_TEMP_3] = kernel_trigger_create(triggerNum); // trigger
_G(globals)[GLB_TEMP_4] = duration << 16; // frame duration (-1=forever, 0=default)
_G(globals)[GLB_TEMP_5] = index << 16; // index of series to show
_G(globals)[GLB_TEMP_6] = (s << 16) / 100; // scale
_G(globals)[GLB_TEMP_7] = x << 16; // x
_G(globals)[GLB_TEMP_8] = y << 16; // y
_G(globals)[GLB_TEMP_14] = (flags & SERIES_STICK) ? 0x10000 : 0; // stick to screen after trigger?
_G(globals)[GLB_TEMP_16] = (flags & SERIES_HORZ_FLIP) ? 0x10000 : 0;// horizontal flip
machine *m = kernel_spawn_machine(seriesName, HASH_SERIES_SHOW_MACHINE, series_trigger_dispatch_callback);
if (!m)
error_show(FL, 'WSMF', seriesName);
return m;
}
machine *series_place_sprite(const char *seriesName, int32 index, int32 x, int32 y, int32 s, int32 layer) {
return series_show(seriesName, layer, 0x40, -1, -1, index, s, x, y);
}
machine *series_show_sprite(const char *seriesName, int32 index, int32 layer) {
return series_show(seriesName, layer, 0x40, -1, -1, index);
}
machine *series_play(const char *seriesName, frac16 layer, uint32 flags, int16 triggerNum,
int32 frameRate, int32 loopCount, int32 s, int32 x, int32 y,
int32 firstFrame, int32 lastFrame) {
int32 myAssetIndex;
RGB8 *tempPalettePtr = nullptr;
term_message(seriesName);
if (flags & SERIES_LOAD_PALETTE)
tempPalettePtr = &_G(master_palette)[0];
if ((myAssetIndex = AddWSAssetCELS(seriesName, -1, tempPalettePtr)) < 0)
error_show(FL, 'SPNF', seriesName);
_G(globals)[GLB_TEMP_1] = (frac16)myAssetIndex << 24; // cels hash
_G(globals)[GLB_TEMP_2] = layer << 16; // layer
_G(globals)[GLB_TEMP_3] = kernel_trigger_create(triggerNum); // trigger
_G(globals)[GLB_TEMP_4] = frameRate << 16; // framerate
_G(globals)[GLB_TEMP_5] = loopCount << 16; // loop count
_G(globals)[GLB_TEMP_6] = (s << 16) / 100; // scale
_G(globals)[GLB_TEMP_7] = x << 16; // x
_G(globals)[GLB_TEMP_8] = y << 16; // y
_G(globals)[GLB_TEMP_9] = firstFrame << 16; // first frame
_G(globals)[GLB_TEMP_10] = lastFrame << 16; // last frame
_G(globals)[GLB_TEMP_11] = (flags & SERIES_PINGPONG) ? 0x10000 : 0; // ping pong
_G(globals)[GLB_TEMP_12] = (flags & SERIES_BACKWARD) ? 0x10000 : 0; // backwards
_G(globals)[GLB_TEMP_13] = (flags & SERIES_RANDOM) ? 0x10000 : 0; // random
_G(globals)[GLB_TEMP_14] = (flags & SERIES_STICK) ? 0x10000 : 0; // stick to screen
_G(globals)[GLB_TEMP_15] = (flags & SERIES_LOOP_TRIGGER) ? 0x10000 : 0; // trigger back every loop?
_G(globals)[GLB_TEMP_16] = (flags & SERIES_HORZ_FLIP) ? 0x10000 : 0; // horizontal flip
machine *m = kernel_spawn_machine(seriesName, HASH_SERIES_PLAY_MACHINE, series_trigger_dispatch_callback);
if (!m)
error_show(FL, 'WSMF', seriesName);
return m;
}
machine *series_ranged_play(const char *seriesName, int32 loopCount, uint32 flags,
int32 firstFrame, int32 lastFrame, int32 s, uint32 layer,
int32 frameRate, int32 trigger, bool stickWhenDone) {
if (loopCount == 1)
loopCount = 0;
if (stickWhenDone)
flags |= 0x10;
return series_play(seriesName, layer, flags, trigger, frameRate,
loopCount, s, 0, 0, firstFrame, lastFrame);
}
machine *series_ranged_play_xy(const char *seriesName, int loopCount, int flags,
int firstFrame, int lastFrame, int x, int y, int s, int layer,
int frameRate, int trigger, bool stick_when_done) {
if (loopCount == 1)
loopCount = 0;
if (stick_when_done)
flags |= 0x10;
return series_play(seriesName, layer, flags, trigger, frameRate,
loopCount, s, x, y, firstFrame, lastFrame);
}
machine *series_plain_play(const char *seriesName, int32 loopCount, uint32 flags,
int32 s, int32 layer, int32 frameRate, int32 trigger, bool stickWhenDone) {
if (stickWhenDone)
flags |= 0x10;
if (loopCount == 1)
loopCount = 0;
return series_play(seriesName, layer, flags, trigger, frameRate, loopCount, s);
}
machine *series_play_xy(const char *seriesName, int loopCount, int flags,
int x, int y, int scale, int layer, int frameRate, int trigger) {
if (loopCount == 1)
loopCount = 0;
return series_play(seriesName, layer, flags, trigger, frameRate,
loopCount, scale, x, y);
}
machine *series_simple_play(const char *seriesName, frac16 layer, bool stickWhenDone) {
int flags = 0;
if (stickWhenDone)
flags |= 0x10;
return series_play(seriesName, layer, flags);
}
} // namespace M4

View File

@@ -0,0 +1,126 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_SERIES_H
#define M4_GRAPHICS_GR_SERIES_H
#include "m4/m4_types.h"
#include "m4/wscript/ws_machine.h"
namespace M4 {
constexpr uint32 SERIES_FORWARD = 0;
constexpr uint32 SERIES_PINGPONG = 1;
constexpr uint32 SERIES_BACKWARD = 2;
constexpr uint32 SERIES_RANDOM = 4; // series is played in random order, trigger after number of frames in range played
constexpr uint32 SERIES_NO_TOSS = 8; // series is not tossed at the end of playing
constexpr uint32 SERIES_STICK = 16; // series sticks on last frame, then sends trigger
constexpr uint32 SERIES_LOOP_TRIGGER = 32; // get trigger back every loop
constexpr uint32 SERIES_LOAD_PALETTE = 64; // load master_palette with colours?
constexpr uint32 SERIES_HORZ_FLIP = 128; // horizontal flip
// Old constants
constexpr uint32 FORWARD = 0;
constexpr uint32 PINGPONG = 1;
constexpr uint32 BACKWARD = 2;
constexpr uint32 STICK = 4;
constexpr uint32 NO_TOSS = 8;
enum {
HASH_SERIES_PLAY_MACHINE = 0,
HASH_SERIES_SHOW_MACHINE = 1,
// HASH_TIMER_MACHINE = 2, // defined in adv.h
HASH_STREAM_MACHINE = 6
};
/**
* Since series are normally started in pairs, this simplifies doing so
*/
struct Series {
machine *_series = nullptr;
machine *_seriesS = nullptr;
void play(const char *seriesName, frac16 layer, uint32 flags = 0,
int16 triggerNum = -1, int32 frameRate = 6, int32 loopCount = 0, int32 s = 100,
int32 x = 0, int32 y = 0, int32 firstFrame = 0, int32 lastFrame = -1);
void show(const char *seriesName, frac16 layer, uint32 flags = 0,
int16 triggerNum = -1, int32 duration = -1, int32 index = 0, int32 s = 100,
int32 x = 0, int32 y = 0);
void show(const char *series1, const char *series2, int layer);
void show_index2(const char *series1, const char *series2, int layer, int index1, int index2);
void terminate();
operator bool() const {
return _series != nullptr;
}
machine *&operator[](uint idx) {
return (idx == 0) ? _series : _seriesS;
}
frac16 *regs() const {
return _series->myAnim8->myRegs;
}
static void series_play(const char *seriesName, frac16 layer, uint32 flags = 0,
int16 triggerNum = -1, int32 frameRate = 6, int32 loopCount = 0, int32 s = 100,
int32 x = 0, int32 y = 0, int32 firstFrame = 0, int32 lastFrame = -1);
static void series_show(const char *seriesName, frac16 layer, uint32 flags = 0,
int16 triggerNum = -1, int32 duration = -1, int32 index = 0, int32 s = 100,
int32 x = 0, int32 y = 0);
};
int32 series_load(const char *seriesName, int32 assetIndex = -1, RGB8 *myPal = nullptr);
void series_unload(int32 assetIndex);
bool series_draw_sprite(int32 spriteHash, int32 index, Buffer *destBuff, int32 x, int32 y);
bool series_show_frame(int32 spriteHash, int32 index, Buffer *destBuff, int32 x, int32 y);
machine *series_place_sprite(const char *seriesName, int32 index, int32 x, int32 y, int32 s, int32 layer);
machine *series_show_sprite(const char *seriesName, int32 index, int32 layer);
machine *series_play(const char *seriesName, frac16 layer, uint32 flags = 0,
int16 triggerNum = -1, int32 frameRate = 6, int32 loopCount = 0, int32 s = 100,
int32 x = 0, int32 y = 0, int32 firstFrame = 0, int32 lastFrame = -1);
machine *series_simple_play(const char *seriesName, frac16 layer, bool stickWhenDone);
machine *series_show(const char *seriesName, frac16 layer, uint32 flags = 0,
int16 triggerNum = -1, int32 duration = -1, int32 index = 0, int32 s = 100,
int32 x = 0, int32 y = 0);
machine *series_ranged_play(const char *seriesName, int32 loopCount, uint32 flags,
int32 firstFrame, int32 lastFrame, int32 s, uint32 layer,
int32 frameRate, int32 trigger = -1, bool stick_when_done = false);
machine *series_ranged_play_xy(const char *seriesName, int loopCount, int flags,
int firstFrame, int lastFrame, int x, int y, int s, int layer,
int frameRate, int trigger = -1, bool stick_when_done = false);
machine *series_plain_play(const char *seriesName, int32 loopCount, uint32 flags,
int32 s, int32 layer, int32 frameRate, int32 trigger = -1, bool stickWhenDone = false);
machine *series_play_xy(const char *seriesName, int loopCount, int flags,
int x, int y, int scale, int layer, int frameRate, int trigger);
machine *series_stream(const char *seriesName, int32 frameRate, int32 layer, int32 trigger);
bool series_stream_break_on_frame(machine *m, int32 frameNum, int32 trigger);
void series_set_frame_rate(machine *m, int32 newFrameRate);
} // namespace M4
#endif

View File

@@ -0,0 +1,262 @@
/* 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 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/>.
*
*/
#include "common/textconsole.h"
#include "m4/graphics/gr_sprite.h"
#include "m4/graphics/gr_surface.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/core/term.h"
#include "m4/mem/memman.h"
#include "m4/platform/draw.h"
namespace M4 {
/**
* ScaleX and ScaleY are supposed to be percents, where 100 means 100%
* S and D are Raw encoded (unencoded!) buffers.
*/
static uint8 scale_sprite(Buffer *S, Buffer *D, uint32 ScaleX, uint32 ScaleY) {
if (!D)
error_show(FL, 'BUF!', "scale sprite NULL D");
if (!S)
error_show(FL, 'BUF!', "scale sprite h:%d w:%d sx:%uld sy:%uld", D->h, D->w, ScaleX, ScaleY);
uint8 *pData = S->data;
/* calculate new x size */
D->w = S->w * ScaleX / 100;
if (S->w * ScaleX % 100 >= 50)
++D->w;
/* calculate new y size */
D->h = S->h * ScaleY / 100;
if (S->h * ScaleY % 100 >= 50)
++D->h;
D->stride = D->w;
/* allocate 'scaled' buffer */
uint8 *pScaled = (uint8 *)mem_alloc(D->h * D->stride, "scaled buffer");
if (!pScaled)
error_show(FL, 'OOM!', "scaled buffer h:%uld w:%uld", D->h, D->stride);
D->data = pScaled;
uint16 ErrY = 50;
for (uint16 i = 0; i < S->h; ++i) {
ErrY += ScaleY;
while (ErrY >= 100) {
uint16 ErrX = 50;
for (uint16 j = 0; j < S->w; ++j) {
ErrX += ScaleX;
while (ErrX >= 100) {
*pScaled++ = *pData;
ErrX -= 100;
}
++pData;
}
ErrY -= 100;
pData -= S->w;
}
pData += S->w;
}
return 0;
}
#define Scaled ((drawReq->scaleY != 100) || (drawReq->scaleX != 100 && drawReq->scaleX != -100))
#define Rle (source.encoding == RLE8)
#define Clipped ((drawReq->x < 0) || (drawReq->y < 0) || (drawReq->x + source.w > drawReq->Dest->w) || (drawReq->y + source.h > drawReq->Dest->h))
#define Forward (drawReq->scaleX > 0)
#define Depthed (drawReq->srcDepth)
#define Shadow (source.encoding & 0x80)
#define ClipD (leftOffset || rightOffset || bottomCut)
uint8 gr_sprite_draw(DrawRequest *drawReq) {
Buffer source;
uint8 *shadowBuff = nullptr, *scaledBuff = nullptr;
Buffer afterScaled = { 0, 0, nullptr, 0, 0 };
if (!drawReq->Src) {
term_message("nullptr source data in sprite_draw");
return 0;
}
// Negative scaleY means don't bother drawing this sprite
if (drawReq->scaleY <= 0)
return 0;
if (!drawReq->Src->w || !drawReq->Src->h)
return 1;
// Copy DrawReq->Src to source buffer
source = *drawReq->Src;
assert(source.data);
// if it's RLE encoded, ensure the sprite will decode to match the expected size
if (source.encoding & RLE8) {
if (RLE8Decode_Size(source.data, source.stride) != (size_t)(source.stride * source.h))
error_show(FL, 'RLE8', "RLE8 sprite suspected BAD!");
}
// Check for RLE encoding in case of shadows
// There is no RLE shadow draw routine, so we have to decode shadows ahead of time.
if ((source.encoding & RLE8) && (source.encoding & SHADOW)) {
if (!(shadowBuff = (uint8 *)mem_alloc(source.stride * source.h, "shadow buff")))
error_show(FL, 'OOM!', "buffer w:%uld, h:%uld", source.w, source.h);
RLE8Decode(source.data, shadowBuff, source.stride);
source.data = shadowBuff;
source.encoding &= ~RLE8;
}
// Check for scaling
// We scale before we draw
if (Scaled) {
// Check if input is RLE8 encoded
// If it's scaled we decode it first
if (Rle) {
if (!(scaledBuff = (uint8 *)mem_alloc(source.stride * source.h, "scaled buffer")))
error_show(FL, 'OOM!', "no mem: buffer w:%d, h:%d", source.w, source.h);
RLE8Decode(source.data, scaledBuff, source.stride);
source.data = scaledBuff;
source.encoding &= ~RLE8;
}
if (scale_sprite(&source, &afterScaled, imath_abs(drawReq->scaleX), imath_abs(drawReq->scaleY))) {
if (shadowBuff)
mem_free(shadowBuff);
if (scaledBuff)
mem_free(scaledBuff);
error_show(FL, 'SPSF', "gr_sprite_draw");
}
// Preserve encoding
afterScaled.encoding = source.encoding;
// Copy AfterScaled to source buffer
source = afterScaled;
}
const bool shadow = (drawReq->Src->encoding & SHADOW) != 0;
assert(!shadow || drawReq->ICT);
M4Surface dst(*drawReq->Dest);
dst.draw(source, drawReq->x, drawReq->y, drawReq->scaleX > 0,
drawReq->srcDepth ? drawReq->depthCode : nullptr, drawReq->srcDepth,
shadow ? drawReq->ICT : nullptr,
drawReq->Pal);
if (shadowBuff)
mem_free(shadowBuff);
if (scaledBuff)
mem_free(scaledBuff);
if (afterScaled.data)
mem_free(afterScaled.data);
return 0;
}
//----------------------------------------------------------------------------------------
//RLE8 COMPRESSION CODE...
#define ESC ((uint8)0)
#define EOL ((uint8)0)
#define EOB ((uint8)1)
#define DELTA ((uint8)2)
#define OutBuffSize(x) ((x) + (((x) + 254) / 255 + 1) * 2 + 2)
static uint16 EncodeScan(uint8 *pi, uint8 *po, uint16 scanlen, uint8 EndByte) {
uint8 *ps = pi + 1;
uint16 outlen = 0, run;
while (scanlen) {
uint16 limit = (scanlen < 255) ? scanlen : 255;
for (run = 1; run < limit && *pi == *ps; ++run, ++ps) {}
if (run > 1) {
scanlen -= run;
*po++ = run;
*po++ = *pi;
outlen += 2;
pi = ps++;
} else if (scanlen < 3) {
for (; scanlen; --scanlen) {
*po++ = 1;
*po++ = *pi++;
outlen += 2;
}
} else {
--ps;
do {
++ps;
while ((*ps != *(ps + 1) || *ps != *(ps + 2) || *ps != *(ps + 3)) && (ps - pi) < limit)
++ps;
} while ((run = ps - pi) < 3);
scanlen -= run;
*po++ = ESC;
*po++ = run;
outlen += (run + 2);
for (limit = 0; limit < run; ++limit)
*po++ = *pi++;
++ps;
}
}
*po++ = ESC;
*po = EndByte;
outlen += 2;
return outlen;
}
uint32 gr_sprite_RLE8_encode(Buffer *Source, Buffer *Dest) {
int i;
uint32 Offset = 0;
Dest->w = Source->w;
Dest->h = Source->h;
Dest->encoding = RLE8;
Dest->stride = Source->stride;
Dest->data = (uint8 *)mem_alloc(Source->h * OutBuffSize(Source->stride), "sprite data");
if (!Dest->data) {
return 0;
}
for (i = 0; i < Source->h - 1; ++i)
Offset += EncodeScan(Source->data + i * Source->stride, Dest->data + Offset, Source->w, EOL);
Offset += EncodeScan(Source->data + i * Source->stride, Dest->data + Offset, Source->w, EOB);
Dest->data = (uint8 *)mem_realloc(Dest->data, Offset, "rle8 sprite data");
return Offset;
}
} // namespace M4

View File

@@ -0,0 +1,54 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_SPRITE_H
#define M4_GRAPHICS_GR_SPRITE_H
#include "m4/m4_types.h"
namespace M4 {
enum {
NO_COMPRESS = 0x00,
RLE8 = 0x01,
SHADOW = 0x80
};
struct DrawRequest {
Buffer *Src = nullptr; // sprite source buffer
Buffer *Dest = nullptr; // destination buffer
int32 x = 0; // x position relative to Destination(0, 0)
int32 y = 0; // y position relative to Destination(0, 0)
int32 scaleX = 0; // x scale factor (can be negative for reverse draw)
int32 scaleY = 0; // y scale factor (can't be negative)
uint8 *depthCode = nullptr; // depth code array for destination (doesn't care if srcDepth is 0)
uint8 *Pal = nullptr; // palette for shadow draw (doesn't care if SHADOW bit is not set in Src.encoding)
uint8 *ICT = nullptr; // Inverse Color Table (doesn't care if SHADOW bit is not set in Src.encoding)
uint8 srcDepth = 0; // depth code for source (0 if no depth processing)
};
uint32 gr_sprite_RLE8_encode(Buffer *Source, Buffer *Dest);
uint8 gr_sprite_draw(DrawRequest *DrawReq);
} // namespace M4
#endif

View File

@@ -0,0 +1,194 @@
/* 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 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/>.
*
*/
#include "common/algorithm.h"
#include "m4/graphics/gr_surface.h"
#include "m4/graphics/gr_sprite.h"
namespace M4 {
M4Surface::M4Surface(int sw, int sh) : Buffer() {
this->w = sw;
this->h = sh;
this->stride = sw;
this->encoding = NO_COMPRESS;
this->data = new byte[sw * sh];
Common::fill(this->data, this->data + sw * sh, 0);
_disposeAfterUse = DisposeAfterUse::YES;
}
M4Surface::M4Surface(const byte *src, int sw, int sh) {
this->w = sw;
this->h = sh;
this->stride = sw;
this->encoding = NO_COMPRESS;
this->data = new byte[sw * sh];
Common::fill(this->data, this->data + sw * sh, 0);
_disposeAfterUse = DisposeAfterUse::YES;
rleDraw(src);
}
M4Surface::~M4Surface() {
if (_disposeAfterUse == DisposeAfterUse::YES)
delete[] data;
}
void M4Surface::rleDraw(const byte *src, int x, int y) {
const byte *srcP = src;
byte *destData = data + y * w + x;
byte *destP = destData;
int destWidth = w;
byte count, val;
int line = 0;
assert(x >= 0 && y >= 0 && x < w && y < h);
for (;;) {
count = *srcP++;
if (count) {
// Basic run length
val = *srcP++;
// 0 pixels are transparent, and are skipped. Otherwise, draw pixels
if (val != 0)
Common::fill(destP, destP + count, val);
destP += count;
} else {
count = *srcP++;
if (count >= 3) {
// Block of uncompressed pixels to copy
for (; count > 0; --count, ++destP) {
val = *srcP++;
if (val != 0)
*destP = val;
}
} else if (!(count & 3)) {
// End of line code
++line;
destP = destData + line * destWidth;
} else {
// Stop drawing image. Seems weird that it doesn't handle the X/Y offset
// form for count & 2, but the original explicitly doesn't implement it
break;
}
}
}
assert(destP <= (data + h * stride));
}
void M4Surface::draw(const Buffer &src, int x, int y, bool forwards,
const byte *depthCodes, int srcDepth, const byte *inverseColorTable,
const byte *palette) {
if ((src.encoding & 0x7f) == RLE8) {
// The standard case of RLE sprite drawing onto screen can directly
// use RLE decompression for performance
if (forwards && !depthCodes && !inverseColorTable && x >= 0 && y >= 0 &&
(x + src.w) <= this->w && (y + src.h) <= this->h) {
rleDraw(src.data, x, y);
} else {
// All other RLE drawing first decompresses the sprite, and then does
// the various clipping, reverse, etc. on that
M4Surface tmp(src.data, src.w, src.h);
drawInner(tmp, depthCodes, x, y, forwards, srcDepth, palette, inverseColorTable);
}
} else {
// Uncompressed images get passed to inner drawing
drawInner(src, depthCodes, x, y, forwards, srcDepth, palette, inverseColorTable);
}
}
void M4Surface::drawInner(const Buffer &src, const byte *depthCodes, int x, int y,
bool forwards, int srcDepth, const byte *palette, const byte *inverseColorTable) {
assert((src.encoding & 0x7f) == NO_COMPRESS);
for (int srcY = 0; srcY < src.h; ++srcY, ++y) {
if (y >= h)
// Below bottom of screen
break;
else if (y < 0)
// Above top of screen
continue;
const byte *srcP = forwards ? src.getBasePtr(0, srcY) : src.getBasePtr(src.w - 1, srcY);
byte *destP = getBasePtr(x, y);
const byte *depthP = depthCodes ? depthCodes + y * w + x : nullptr;
int deltaX = forwards ? 1 : -1;
int destX = x;
uint32 adjusted, total;
for (int srcX = 0; srcX < src.w; ++srcX, srcP += deltaX, ++destX) {
if (destX >= w)
// Beyond right of screen
break;
byte v = *srcP;
byte depth = depthP ? *depthP & 0xf : 0;
if (destX >= 0 && v != 0 && (!depthP || depth == 0 || srcDepth < depth)) {
if (inverseColorTable) {
// Handling for shadows
if (v != 128) {
const byte *palP = palette + *destP * 3;
uint rgb = (uint32)palP[0] | ((uint32)palP[1] << 8) |
((uint32)palP[2] << 16);
rgb >>= 2;
// Red component
adjusted = (rgb & 0xff) * v;
adjusted = MIN((uint)(adjusted >> 8), 31U);
total = adjusted << 10;
// Green component
rgb >>= 8;
adjusted = (rgb & 0xff) * v;
adjusted = MIN((uint)(adjusted >> 8), 31U);
total |= (adjusted << 5);
// Blue component
rgb >>= 8;
adjusted = (rgb & 0xff) * v;
adjusted = MIN((uint)(adjusted >> 8), 31U);
total |= adjusted;
// Write out pixel from inverse table
*destP = inverseColorTable[total];
}
} else {
// Normal pixel
*destP = v;
}
}
++destP;
if (depthP)
++depthP;
}
}
}
} // namespace M4

View File

@@ -0,0 +1,61 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GR_SURFACE_H
#define M4_GRAPHICS_GR_SURFACE_H
#include "common/types.h"
#include "m4/m4_types.h"
namespace M4 {
class M4Surface : public Buffer {
private:
DisposeAfterUse::Flag _disposeAfterUse = DisposeAfterUse::NO;
void drawInner(const Buffer &src, const byte *depthCodes, int x, int y,
bool forwards, int srcDepth, const byte *palette, const byte *inverseColorTable);
public:
M4Surface() : Buffer() {}
M4Surface(const Buffer &src) : Buffer(src) {}
M4Surface(int sw, int sh);
M4Surface(const byte *src, int sw, int sh);
~M4Surface();
/**
* Simple drawing at a given position given source RLE data.
* In this simplified version, the sprite must be entirely on-screen
*/
void rleDraw(const byte *src, int x = 0, int y = 0);
/**
* Main drawing
*/
void draw(const Buffer &src, int x, int y, bool forwards = true,
const byte *depthCodes = nullptr, int srcDepth = -1,
const byte *inverseColorTable = nullptr, const byte *palette = nullptr);
};
} // namespace M4
#endif

View File

@@ -0,0 +1,88 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_GRAPHICS_H
#define M4_GRAPHICS_GRAPHICS_H
#include "common/scummsys.h"
namespace M4 {
constexpr int SCREEN_WIDTH = 640;
constexpr int SCREEN_HEIGHT = 480;
#define FILL_INTERIOR 1 // a flag for use by DrawTile
#define BORDER 0
#define COLOR_MAX_SHADOW_COLORS 3
#define FONT_SIZE 128
#define FONT_MAX_WIDTH 255
#define FONT_MAX_HEIGHT 200
//SS FILE DATA DEFINITIONS...
#define HEAD_M4SS 0x4D345353 //'M4SS'
#define HEAD_SS4M 0x5353344D //'SS4M'
#define SS_FORMAT 101 //if it ever has to be printed, divide by 100
#define CELS__PAL 0x2050414C //' PAL'
#define CELS_LAP_ 0x4C415020 //INTEL ' PAL'
#define CELS___SS 0x20205353 //' SS'
#define CELS_SS__ 0x53532020 //INTEL ' SS'
#define CELS_HEADER 0
#define CELS_SRC_SIZE 1
#define CELS_PACKING 2
#define CELS_FRAME_RATE 3
#define CELS_PIX_SPEED 4
#define CELS_SS_MAX_W 5
#define CELS_SS_MAX_H 6
#define CELS_RSVD_3 7
#define CELS_RSVD_4 8
#define CELS_RSVD_5 9
#define CELS_RSVD_6 10
#define CELS_RSVD_7 11
#define CELS_RSVD_8 12
#define CELS_COUNT 13
#define SS_HEAD_SIZE 14 //includes all the previous dwords
#define CELS_OFFSETS 14
#define CELS_PACK 0
#define CELS_STREAM 1
#define CELS_X 2
#define CELS_Y 3
#define CELS_W 4
#define CELS_H 5
#define CELS_COMP 6
#define INDV_RSVD_1 8
#define INDV_RSVD_2 8
#define INDV_RSVD_3 9
#define INDV_RSVD_4 10
#define INDV_RSVD_5 11
#define INDV_RSVD_6 12
#define INDV_RSVD_7 13
#define INDV_RSVD_8 14
#define SS_INDV_HEAD 15 //includes all the previous dwords
#define CELS_DATA 15
} // namespace M4
#endif

View File

@@ -0,0 +1,862 @@
/* 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 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/>.
*
*/
#include "m4/m4_types.h"
#include "m4/adv_r/adv_control.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/graphics/krn_pal.h"
#include "m4/graphics/gr_pal.h"
#include "m4/graphics/gr_series.h"
#include "m4/gui/gui_sys.h"
#include "m4/gui/gui_vmng.h"
#include "m4/platform/keys.h"
#include "m4/vars.h"
#include "m4/m4.h"
#include "m4/platform/timer.h"
namespace M4 {
#define _GP(X) _G(krnPal)._##X
#define BACKGROUND_HEIGHT (int32)639
#define GREY_START (IS_RIDDLE ? 21 : 32)
#define GREY_END (IS_RIDDLE ? 58 : 63)
#define NUM_GREYS (1 + GREY_END - GREY_START)
#define FREE_START (GREY_END + 1)
#define FREE_END 255
#define NUM_FREE (255 - FREE_START + 1)
static HotkeyCB remember_esc_key;
void krn_pal_game_task() {
g_engine->pal_game_task();
}
static int32 screen_height(Buffer *grey_screen) {
return imath_min(BACKGROUND_HEIGHT + _G(kernel).letter_box_y, grey_screen->h);
}
static void grey_fade(RGB8 *pal, int32 to_from_flag, int32 from, int32 to, int32 steps, int32 delay) {
RGB8 *working = (RGB8 *)mem_alloc(sizeof(RGB8) * 256, STR_FADEPAL);
// perform the fade
for (int i = 1; i < steps; i++) {
for (int j = from; j <= to; j++) {
if (to_from_flag == TO_GREY) { // fade to grey from full color
working[j].r = (Byte)((int)pal[j].r + ((((int)_GP(fadeToMe)[j].r - (int)pal[j].r) * i) / steps));
working[j].g = (Byte)((int)pal[j].g + ((((int)_GP(fadeToMe)[j].g - (int)pal[j].g) * i) / steps));
working[j].b = (Byte)((int)pal[j].b + ((((int)_GP(fadeToMe)[j].b - (int)pal[j].b) * i) / steps));
} else if (to_from_flag == TO_COLOR) { // fade from grey to full color
working[j].r = (Byte)((int)_GP(fadeToMe)[j].r + ((((int)pal[j].r - (int)_GP(fadeToMe)[j].r) * i) / steps));
working[j].g = (Byte)((int)_GP(fadeToMe)[j].g + ((((int)pal[j].g - (int)_GP(fadeToMe)[j].g) * i) / steps));
working[j].b = (Byte)((int)_GP(fadeToMe)[j].b + ((((int)pal[j].b - (int)_GP(fadeToMe)[j].b) * i) / steps));
} else { //fade from grey to black
working[j].r = (Byte)((int)_GP(fadeToMe)[j].r - ((((int)_GP(fadeToMe)[j].r) * i) / steps));
working[j].g = (Byte)((int)_GP(fadeToMe)[j].g - ((((int)_GP(fadeToMe)[j].g) * i) / steps));
working[j].b = (Byte)((int)_GP(fadeToMe)[j].b - ((((int)_GP(fadeToMe)[j].b) * i) / steps));
}
}
gr_pal_set_range(working, from, to - from + 1); ///set pal 21-255
// Time delay of "delay" milliseconds
g_events->delay(delay);
}
// Eliminate round off error
if (to_from_flag == TO_GREY) {
gr_pal_set_range(_GP(fadeToMe), from, to - from + 1); ///set pal 21-255
} else if (to_from_flag == TO_COLOR) {
gr_pal_set_range(pal, from, to - from + 1); ///set pal 21-255
} else {
for (int i = from; i <= to; i++) {
pal[i].r = pal[i].g = pal[i].b = 0;
}
gr_pal_set_range(pal, from, to - from + 1); ///set pal 21-255
}
mem_free(working);
}
// screen is the currently displayed screen
// screenPicture is the data to restore the screen with
// note: color 0 doesn't fade.
static void create_luminance_map(RGB8 *pal) {
for (int i = GREY_START; i <= FREE_END; i++) {
const Byte luminance = (Byte)((pal[i].r + pal[i].g + pal[i].b) / 3);
_GP(fadeToMe)[i].g = luminance;
// Orion Burger uses green shading, Riddle uses grey shading
_GP(fadeToMe)[i].r = _GP(fadeToMe)[i].b = IS_RIDDLE ? luminance : 0;
}
}
// finds the best matches for the in the greys in the grey ramp range using the free range greys
// used to map greys out of the grey ramp area, and then again to map the grey ramp out of the grey ramp area!
static void make_translation_table(RGB8 *pal) {
for (int32 i = 0; i < NUM_GREYS; i++) {
int32 bestMatch = FREE_START; // assume the first of the free indexes is best match to start with
int32 minDist = 255; // assume that it's really far away to start with
if (!(i & 0x3ff)) {
digi_read_another_chunk();
midi_loop();
}
// look for best match in the free indexes for the greys in GREY_START-GREY_END range (we need these available)
const int32 matchGrey = pal[GREY_START + i].g; // Use green instead of red cause we're having a green screen
for (int32 j = FREE_START; j <= FREE_END; j++) {
const int32 tryGrey = pal[j].g;
if (imath_abs(tryGrey - matchGrey) < minDist) {
minDist = imath_abs(tryGrey - matchGrey);
bestMatch = j;
}
if (minDist == 0)
break; // no need to continue searching if we found a perfect match
}
_GP(translation)[i] = (uint8)bestMatch;
}
}
void krn_fade_to_grey(RGB8 *pal, int32 steps, int32 delay) {
if (_G(kernel).fading_to_grey) {
return;
}
_G(kernel).fading_to_grey = true;
Buffer *grey_screen = _G(gameDrawBuff)->get_buffer();
_GP(fadeToMe) = (RGB8 *)mem_alloc(sizeof(RGB8) * 256, STR_FADEPAL);
_GP(trick) = (RGB8 *)mem_alloc(sizeof(RGB8) * 256, STR_FADEPAL);
_GP(picPal) = (RGB8 *)mem_alloc(sizeof(RGB8) * 256, STR_FADEPAL);
memcpy(_GP(picPal), pal, sizeof(RGB8) * 256);
create_luminance_map(pal);
grey_fade(pal, TO_GREY, GREY_START, GREY_END, steps, delay);
// Make translation table to translate colors using entries 59-255 into 21-58 range
for (int32 i = 0; i < (IS_RIDDLE ? 64 : 32); i++) {
int32 bestMatch = IS_RIDDLE ? 63 : 65;
int32 minDist = 255;
for (int32 j = FREE_START; j <= 255; j++) {
if (imath_abs((_GP(fadeToMe)[j].r >> 2) - i) < minDist) {
minDist = imath_abs((_GP(fadeToMe)[j].r >> 2) - i);
bestMatch = j;
}
if (minDist == 0)
// No need to continue searching if we found a perfect match
break;
}
_GP(translation)[i] = (uint8)bestMatch;
}
// Palette now grey scale. Remap any pixels which are in the range 21-58 to the range 53-255
// because we need to use those palette entries soon
uint8 *tempPtr = grey_screen->data;
// Note: this loop should be y0 to y1, x0 to x1, not a stride*h loop.
for (int32 i = 0; i < (grey_screen->stride * grey_screen->h); i++) {
if ((*tempPtr >= GREY_START) && (*tempPtr <= GREY_END)) {
// Must move the pixel index to the best match in FREE_START-FREE_END range with _GP(translation) table
*tempPtr = _GP(translation)[*tempPtr - GREY_START];
}
tempPtr++;
if (!(i & 0x3ff)) {
_G(digi).task();
_G(midi).task();
}
}
RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y);
// Make new trickPal with grey-scale ramp entries and load it into VGA registers
memcpy(_GP(trick), _GP(fadeToMe), sizeof(RGB8) * 256); // trick pal is the greyed version plus the grey ramp overlayed on top
const byte grey_step = 256 / NUM_GREYS;
byte grey_ramp = 0;
for (int32 i = GREY_START; i <= GREY_END; i++) {
_GP(trick)[i].g = grey_ramp;
_GP(trick)[i].r = _GP(trick)[i].b = IS_RIDDLE ? grey_ramp : 0;
grey_ramp += grey_step;
}
gr_pal_set_range(_GP(trick), GREY_START, NUM_GREYS);
remap_buffer_with_luminance_map(grey_screen, 0, 0, grey_screen->w - 1, screen_height(grey_screen) - 1);
_G(gameDrawBuff)->release();
RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y);
}
void krn_fade_from_grey(RGB8 *pal, int32 steps, int32 delay, int32 fadeType) {
if (!_G(kernel).fading_to_grey) {
return;
}
// Get the screen
Buffer *grey_screen = _G(gameDrawBuff)->get_buffer();
// load original faded greys into the free indexes (no pixels have these indexes yet)
gr_pal_set_range(_GP(fadeToMe), FREE_START, NUM_FREE); // Load fadeToMe colors into VGA
make_translation_table(_GP(trick)); // This is used in fade_to_grey too!
// for every pixel in the screen, move any pixel in the GREY_START-GREY_END range out in to the free range
uint8 *tempPtr = grey_screen->data;
// note: this loop should be y0 to y1, x0 to x1, not a stride*h loop.
for (int32 i = 0; i < (grey_screen->stride * grey_screen->h); ++i) {
if (!(i & 0x3ff)) {
_G(digi).task();
_G(midi).task();
}
// if the pixel is within the GREY range, move it to where the _GP(translation) table says
if ((*tempPtr >= GREY_START) && (*tempPtr <= GREY_END)) {
*tempPtr = _GP(translation)[*tempPtr - GREY_START];
}
tempPtr++;
}
// Remapped indexes out of grey ramp
RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y);
// Setting grey ramp indexes back to picture greys
gr_pal_set_range(_GP(fadeToMe), GREY_START, NUM_GREYS); // get the rest of the original re-luminance colors
//recopy screenPicture to screen to restore original pixels
krn_UnsetGreyVideoMode();
RestoreScreens(0, 0, MAX_VIDEO_X, MAX_VIDEO_Y);
memcpy(pal, _GP(picPal), sizeof(RGB8) * 256);
ws_RefreshWoodscriptBuffer(_G(game_bgBuff)->get_buffer(), &(_G(currentSceneDef).depth_table[0]),
_G(screenCodeBuff)->get_buffer(), (uint8 *)&_G(master_palette)[0], _G(inverse_pal)->get_ptr());
_G(game_bgBuff)->release();
_G(inverse_pal)->release();
RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y);
grey_fade(pal, fadeType, GREY_START, FREE_END, steps, delay);
mem_free((char *)_GP(trick));
mem_free((char *)_GP(fadeToMe));
mem_free((char *)_GP(picPal));
_G(kernel).fading_to_grey = false;
_G(gameDrawBuff)->release();
gr_pal_set(_G(master_palette));
}
void kernel_examine_inventory_object(const char *picName, RGB8 *pal, int steps, int delay,
int32 x, int32 y, int32 triggerNum, const char *digiName, int32 digiTrigger) {
remember_esc_key = GetSystemHotkey(KEY_ESCAPE);
RemoveSystemHotkey(KEY_ESCAPE);
interface_hide();
_GP(exam_saved_hotspots) = _G(currentSceneDef).hotspots;
_G(currentSceneDef).hotspots = nullptr;
_GP(myFadeTrigger) = kernel_trigger_create(triggerNum);
krn_fade_to_grey(pal, steps, delay);
_GP(seriesHash) = series_load(picName, -1, pal); // Preload sprite so we can unload it
gr_pal_set_range(pal, FREE_START, NUM_FREE); // Set that series colors into VGA
RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y);
Buffer *grey_screen = _G(gameDrawBuff)->get_buffer();
krn_SetGreyVideoMode(
// Grey rectangle
0, 0, MAX_VIDEO_X, screen_height(grey_screen) + _G(kernel).letter_box_y,
// Color rectangle
x, y, x + ws_get_sprite_width(_GP(seriesHash), 0) - 1, y + ws_get_sprite_height(_GP(seriesHash), 0) - 1);
_G(gameDrawBuff)->release();
// Play the sprite series as a loop
int32 status;
ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status);
_GP(seriesAnim8) = series_play_xy(picName, -1, FORWARD,
x - game_buff_ptr->x1, y - game_buff_ptr->y1, 100, 0, 7, -1);
if (digiName) {
digi_play(digiName, 1, 255, digiTrigger);
}
player_set_commands_allowed(true);
cycleEngines(_G(game_bgBuff)->get_buffer(),
&(_G(currentSceneDef).depth_table[0]),
_G(screenCodeBuff)->get_buffer(),
(uint8 *)&_G(master_palette)[0],
_G(inverse_pal)->get_ptr(), true);
game_pause(true);
_G(inverse_pal)->release();
_G(game_bgBuff)->release();
pauseEngines();
}
void kernel_examine_inventory_object(const char *picName, int steps, int delay,
int32 x, int32 y, int32 triggerNum, const char *digiName, int32 digiTrigger) {
kernel_examine_inventory_object(picName, _G(master_palette), steps, delay,
x, y, triggerNum, digiName, digiTrigger);
}
void kernel_unexamine_inventory_object(RGB8 *pal, int steps, int delay) {
if (!_GP(seriesAnim8) || _GP(seriesHash) < 0)
return;
player_set_commands_allowed(false);
game_pause(false);
unpauseEngines();
terminateMachine(_GP(seriesAnim8));
series_unload(_GP(seriesHash));
_GP(seriesAnim8) = nullptr;
_GP(seriesHash) = 0;
Buffer *grey_screen = _G(gameDrawBuff)->get_buffer();
krn_SetGreyVideoMode(0, 0, MAX_VIDEO_X, screen_height(grey_screen) + _G(kernel).letter_box_y, -1, -1, -1, -1);
_G(gameDrawBuff)->release();
krn_pal_game_task();
krn_fade_from_grey(pal, steps, delay, TO_COLOR);
krn_pal_game_task();
// Set in kernel_examine_inventory_object (above)
kernel_trigger_dispatchx(_GP(myFadeTrigger));
RestoreScreens(0, 0, MAX_VIDEO_X, MAX_VIDEO_Y);
_G(currentSceneDef).hotspots = _GP(exam_saved_hotspots);
interface_show();
AddSystemHotkey(KEY_ESCAPE, remember_esc_key);
}
// This is an inplace remap
// fadeToMe must already have been set up to correspond to the image on the screen
void remap_buffer_with_luminance_map(Buffer *src, int32 x1, int32 y1, int32 x2, int32 y2) {
if ((!src) || (!src->data)) return;
// WORKAROUND: Fix original bounding that could result in buffer overruns on final y2 line
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
if (x2 >= src->w) x2 = src->w - 1;
if (y2 >= src->h) y2 = src->h - 1;
if (x2 <= x1 || y2 <= y1)
return;
x2 -= x1;
y2 -= y1;
for (int32 y = 0; y <= y2; y++) {
uint8 *ptr = &src->data[(y + y1) * src->stride + x1];
for (int32 x = 0; x <= x2; x++) {
// Remap the greyed out pixel to the closest grey in GREY_START to GREY_END range
// shift right 3, takes a 255 value and makes it out of 32 (the number of greys in reduced grey ramp)
ptr[x] = (uint8)(GREY_START + (_GP(fadeToMe)[ptr[x]].g >> 3)); // Use green instead of red cause we're having a green screen
}
if (!(y & 0xff)) {
_G(digi).task();
_G(midi).task();
}
}
}
void krn_SetGreyVideoMode(int32 grey_x1, int32 grey_y1, int32 grey_x2, int32 grey_y2, int32 color_x1, int32 color_y1, int32 color_x2, int32 color_y2) {
_GP(greyAreaX1) = grey_x1;
_GP(greyAreaY1) = grey_y1;
_GP(greyAreaX2) = grey_x2;
_GP(greyAreaY2) = grey_y2;
_GP(colorAreaX1) = color_x1;
_GP(colorAreaY1) = color_y1;
_GP(colorAreaX2) = color_x2;
_GP(colorAreaY2) = color_y2;
_GP(greyVideoMode) = true;
}
void krn_UnsetGreyVideoMode() {
_GP(greyAreaX1) = -1;
_GP(greyAreaY1) = -1;
_GP(greyAreaX2) = -1;
_GP(greyAreaY2) = -1;
_GP(colorAreaX1) = -1;
_GP(colorAreaY1) = -1;
_GP(colorAreaX2) = -1;
_GP(colorAreaY2) = -1;
_GP(greyVideoMode) = false;
}
bool krn_GetGreyMode() {
return _GP(greyVideoMode);
}
void krn_UpdateGreyArea(Buffer *greyOutThisBuffer, int32 scrnX, int32 scrnY, int32 greyX1, int32 greyY1, int32 greyX2, int32 greyY2) {
if ((!_GP(greyVideoMode)) || (!greyOutThisBuffer) || (!greyOutThisBuffer->data)) {
return;
}
int32 x1 = imath_max(greyX1 + scrnX, _GP(greyAreaX1));
int32 y1 = imath_max(greyY1 + scrnY, _GP(greyAreaY1));
const int32 x2 = imath_min(greyX2 + scrnX, _GP(greyAreaX2));
int32 y2 = imath_min(greyY2 + scrnY, _GP(greyAreaY2));
if ((x1 > x2) || (y1 > y2)) return;
bool finished = false;
if (!finished) {
if (y1 < _GP(colorAreaY1)) {
remap_buffer_with_luminance_map(greyOutThisBuffer,
x1 - scrnX, y1 - scrnY, x2 - scrnX, imath_min(y2, _GP(colorAreaY1) - 1) - scrnY);
y1 = imath_min(y2, _GP(colorAreaY1));
if (y1 >= y2)
finished = true;
}
}
if (!finished) {
if (y2 > _GP(colorAreaY2)) {
remap_buffer_with_luminance_map(greyOutThisBuffer,
x1 - scrnX, imath_max(y1, _GP(colorAreaY2) + 1) - scrnY, x2 - scrnX, y2 - scrnY);
y2 = imath_max(y1, _GP(colorAreaY2));
if (y1 >= y2)
finished = true;
}
}
if (!finished) {
if (x1 < _GP(colorAreaX1)) {
remap_buffer_with_luminance_map(greyOutThisBuffer,
x1 - scrnX, y1 - scrnY, imath_min(x2, _GP(colorAreaX1) - 1) - scrnX, y2 - scrnY);
x1 = imath_min(x2, _GP(colorAreaX1));
if (x1 >= x2)
finished = true;
}
}
if (!finished) {
if (x2 > _GP(colorAreaX2)) {
remap_buffer_with_luminance_map(greyOutThisBuffer,
imath_max(x1, _GP(colorAreaX2) + 1) - scrnX, y1 - scrnY, x2 - scrnX, y2 - scrnY);
}
}
}
void krn_ChangeBufferLuminance(Buffer *target, int32 percent) {
uint8 luminancePal[256];
// Parameter verification
if ((!target) || (!target->data)) {
return;
}
if ((percent < 0) || (percent == 100)) {
return;
}
if (percent == 0) {
gr_color_set(__BLACK);
gr_buffer_rect_fill(target, 0, 0, target->w, target->h);
return;
}
// Calculate the frac16 form of the percent
const frac16 fracPercent = (percent * 255) / 100;
// Get the palette and the inverse palette
RGB8 *pal = &_G(master_palette)[0];
uint8 *inverse_palette = _G(inverse_pal)->get_ptr();
if ((!pal) || (!inverse_palette)) {
return;
}
// Calculate the luminance Pal table
for (int32 i = 0; i < 256; i++) {
const int32 r = ((((pal[i].r * fracPercent) >> 10) >> 1)) & 0x1f;
const int32 g = ((((pal[i].g * fracPercent) >> 10) >> 1)) & 0x1f;
const int32 b = ((((pal[i].b * fracPercent) >> 10) >> 1)) & 0x1f;
luminancePal[i] = inverse_palette[(r << 10) + (g << 5) + b];
}
// Note: this loop should be y0 to y1, x0 to x1, not a stride*h loop.
// Loop through every pixel replacing it with the index into the luminance table
uint8 *tempPtr = target->data;
for (int32 y = 0; y < target->h; y++) {
for (int32 x = 0; x < target->stride; x++) {
*tempPtr = luminancePal[*tempPtr];
tempPtr++;
//pixel = *tempPtr;
}
}
_G(inverse_pal)->release();
}
static void pal_fade_callback(frac16 myMessage) {
_G(pal_fade_in_progress) = false;
kernel_trigger_dispatchx((int32)myMessage);
}
void pal_fade_init(RGB8 *origPalette, int32 firstPalEntry, int32 lastPalEntry,
int32 targetPercent, int32 numTicks, int32 triggerNum) {
if ((!origPalette) || (firstPalEntry < 0) || (lastPalEntry > 255) || (firstPalEntry > lastPalEntry))
return;
if ((targetPercent < 0) || (targetPercent > 100))
return;
_GP(myFadeReq) = true;
_GP(myFadeFinished) = false;
_GP(myFadeStartTime) = timer_read_60();
_GP(myFadeEndDelayTime) = timer_read_60();
_GP(myFadeStartIndex) = firstPalEntry;
_GP(myFadeEndIndex) = lastPalEntry;
_GP(myFadeEndTime) = _GP(myFadeStartTime) + numTicks;
_GP(myFadeTrigger) = kernel_trigger_create(triggerNum);
_GP(myFadeStartPercentFrac) = _GP(myFadeCurrPercentFrac);
_GP(myFadePercentFrac) = DivSF16(targetPercent << 16, 100 << 16);
// Disable_end_user_hot_keys();
_G(pal_fade_in_progress) = true;
}
void pal_fade_init(int32 firstPalEntry, int32 lastPalEntry, int32 targetPercent, int32 numTicks, int32 triggerNum) {
pal_fade_init(_G(master_palette), firstPalEntry, lastPalEntry, targetPercent, numTicks, triggerNum);
}
void disable_player_commands_and_fade_init(int trigger) {
player_set_commands_allowed(false);
pal_fade_init(_G(master_palette), _G(kernel).first_fade, 255, 0, 30, trigger);
}
static void pal_fade_update(RGB8 *origPalette) {
const int32 currTime = timer_read_60();
if (currTime >= _GP(myFadeEndDelayTime)) { // If the delay has expired, fade more
frac16 tempFrac2;
if (currTime >= _GP(myFadeEndTime)) {
tempFrac2 = _GP(myFadePercentFrac);
_GP(myFadeStartPercentFrac) = _GP(myFadePercentFrac);
_GP(myFadeFinished) = true;
} else if (currTime <= _GP(myFadeStartTime)) {
return;
} else {
const frac16 tempFrac = DivSF16((currTime - _GP(myFadeStartTime)) << 16, (_GP(myFadeEndTime) - _GP(myFadeStartTime)) << 16);
tempFrac2 = MulSF16(tempFrac, _GP(myFadePercentFrac) - _GP(myFadeStartPercentFrac)) + _GP(myFadeStartPercentFrac);
}
_GP(myFadeCurrPercentFrac) = tempFrac2;
for (int32 i = _GP(myFadeStartIndex); i <= _GP(myFadeEndIndex); i++) {
_GP(myFXPalette)[i].r = (Byte)(MulSF16(origPalette[i].r << 16, tempFrac2) >> 16);
_GP(myFXPalette)[i].g = (Byte)(MulSF16(origPalette[i].g << 16, tempFrac2) >> 16);
_GP(myFXPalette)[i].b = (Byte)(MulSF16(origPalette[i].b << 16, tempFrac2) >> 16);
}
// Recalculate the end delay time again
_GP(myFadeEndDelayTime) = currTime + _GP(myFadeDelayTicks); // Recalculate the end delay time again
// Must refresh the DAC
_GP(myFadeDACrefresh) = true;
}
}
void clear_DAC() {
RGB8 color;
color.r = color.b = color.g = 0;
for (int i = 0; i < 256; i++)
gr_pal_set_entry(i, &color);
}
void pal_fade_set_start(RGB8 *origPalette, int32 percent) {
pal_fade_init(origPalette, _G(kernel).first_fade, 255, percent, 0, (uint)-1);
pal_fade_update(origPalette);
pal_fx_update();
}
void pal_fade_set_start(int32 percent) {
pal_fade_set_start(_G(master_palette), percent);
}
static void pal_cycle_callback(frac16 myMessage) {
kernel_trigger_dispatchx((uint32)myMessage);
}
void pal_cycle_init(int32 firstPalEntry, int32 lastPalEntry,
int32 delayTicks, int32 totalTicks, int32 triggerNum) {
// Validation
if ((firstPalEntry < 0) || (lastPalEntry > 255) || (firstPalEntry > lastPalEntry))
return;
if (delayTicks <= 0)
return;
_GP(myCycleReq) = true;
_GP(myCycleFinished) = false;
_GP(myCycleDelayTicks) = delayTicks;
_GP(myCycleStartTime) = timer_read_60();
_GP(myCycleEndDelayTime) = timer_read_60();
_GP(myCycleStartIndex) = firstPalEntry;
_GP(myCycleEndIndex) = lastPalEntry;
_GP(myCycleTrigger) = kernel_trigger_create(triggerNum); // Returned when myCycleEndTime is reached
if (totalTicks > 0) { // If totalTicks > 0, calculate end time
_GP(myCycleEndTime) = _GP(myCycleStartTime) + totalTicks;
_GP(myCycleNeverStopCycling) = false;
} else if (totalTicks < 0) { // If totalTicks < 0, never stop the cycling
_GP(myCycleNeverStopCycling) = true;
} else { // If totalTicks is 0, stop cycling now
_GP(myCycleReq) = false;
_GP(myCycleFinished) = true;
}
}
bool pal_cycle_active() {
return _GP(myCycleReq);
}
void pal_cycle_stop() {
_GP(myCycleReq) = false;
}
void pal_cycle_resume() {
_GP(myCycleReq) = true;
}
static void pal_cycle_update() {
const int32 currTime = timer_read_60(); // Get current time
if (_GP(myCycleNeverStopCycling) == false) { // If there is an end time to get to...
if (currTime >= _GP(myCycleEndTime)) { // See if we have reached it
_GP(myCycleFinished) = true; // Mark cycling as finished
return; // Return
}
} else {
// See if we should color cycle right now
if (currTime >= _GP(myCycleEndDelayTime)) { // If the delay has expired, color cycle
RGB8 firstColor;
int32 i;
// Cycle the master palette
firstColor.r = _G(master_palette)[_GP(myCycleStartIndex)].r; // Remember first color
firstColor.g = _G(master_palette)[_GP(myCycleStartIndex)].g;
firstColor.b = _G(master_palette)[_GP(myCycleStartIndex)].b;
for (i = _GP(myCycleStartIndex); i < _GP(myCycleEndIndex); ++i) { // Shift colors down one in palette
_G(master_palette)[i].r = _G(master_palette)[i + 1].r;
_G(master_palette)[i].g = _G(master_palette)[i + 1].g;
_G(master_palette)[i].b = _G(master_palette)[i + 1].b;
}
_G(master_palette)[_GP(myCycleEndIndex)].r = firstColor.r; // Set last color to the first color
_G(master_palette)[_GP(myCycleEndIndex)].g = firstColor.g;
_G(master_palette)[_GP(myCycleEndIndex)].b = firstColor.b;
// Then cycle the FX palette
firstColor.r = _GP(myFXPalette)[_GP(myCycleStartIndex)].r; // Remember first color
firstColor.g = _GP(myFXPalette)[_GP(myCycleStartIndex)].g;
firstColor.b = _GP(myFXPalette)[_GP(myCycleStartIndex)].b;
for (i = _GP(myCycleStartIndex); i < _GP(myCycleEndIndex); ++i) { // Shift colors down one in palette
_GP(myFXPalette)[i].r = _GP(myFXPalette)[i + 1].r;
_GP(myFXPalette)[i].g = _GP(myFXPalette)[i + 1].g;
_GP(myFXPalette)[i].b = _GP(myFXPalette)[i + 1].b;
}
_GP(myFXPalette)[_GP(myCycleEndIndex)].r = firstColor.r; // Set last color to the first color
_GP(myFXPalette)[_GP(myCycleEndIndex)].g = firstColor.g;
_GP(myFXPalette)[_GP(myCycleEndIndex)].b = firstColor.b;
// Recalculate the end delay time again
_GP(myCycleEndDelayTime) = currTime + _GP(myCycleDelayTicks); // Recalculate the end delay time again
// must refresh the DAC
_GP(myCycleDACrefresh) = true;
}
}
}
void pal_fx_update() {
int32 startA = 0, endA = 0, startB = 0, endB = 0, startDAC = 0, endDAC = 0;
if (!_GP(myCycleReq) && !_GP(myFadeReq))
// Crap out quickly if no effects required
return;
// Perform any effect required and track index ranges
if (_GP(myCycleReq)) {
pal_cycle_update(); // Do the cycling (cycles master_palette and _GP(myFXPalette))
if (_GP(myCycleDACrefresh)) { // If it needs the DAC to be refreshed,
startA = _GP(myCycleStartIndex); // remember the range
endA = _GP(myCycleEndIndex);
_GP(myCycleDACrefresh) = false;
}
}
if (_GP(myFadeReq)) {
pal_fade_update(&_G(master_palette)[0]); // Do the fading (sets myFXPalette to faded master_palette)
if (_GP(myFadeDACrefresh)) { // If it needs the DAC to be refreshed,
startB = _GP(myFadeStartIndex); // remember the range
endB = _GP(myFadeEndIndex);
_GP(myFadeDACrefresh) = false;
}
}
// Check ranges to perform minimum calls of gr_pal_set_range().
// This was originally done to minimize snow on monitor due to repeated OUT instructions
if (endA < startB || endB < startA) {
// if A and B ranges don't overlap
if (!(startA == 0 && endA == 0)) // if this is not the degenerate case (just the transparent color)
gr_pal_set_range(&_GP(myFXPalette)[0], startA, endA - startA + 1); // set A range of the DAC
if (!(startB == 0 && endB == 0)) // if this is not the degenerate case (just the transparent color)
gr_pal_set_range(&_GP(myFXPalette)[0], startB, endB - startB + 1); // set B range of the DAC
} else {
// They overlap, so find the extent of the overlap
(startA < startB) ? (startDAC = startA) : (startDAC = startB); // which start is less
(endA > endB) ? (endDAC = endA) : (endDAC = endB); // which end is more
if (!(startDAC == 0 && endDAC == 0)) // if this is not the degenerate case (just the transparent color)
gr_pal_set_range(&_GP(myFXPalette)[0], startDAC, endDAC - startDAC + 1); // set the whole range of the DAC
}
// Turn off flags and call callbacks if effects are finished
if (_GP(myFadeReq) && _GP(myFadeFinished)) {
_GP(myFadeReq) = false;
pal_fade_callback(_GP(myFadeTrigger));
}
if (_GP(myCycleReq) && _GP(myCycleFinished)) {
_GP(myCycleReq) = false;
pal_cycle_callback(_GP(myCycleTrigger));
}
}
void DAC_tint_range(const RGB8 *tintColor, int32 percent, int32 firstPalEntry, int32 lastPalEntry, bool transparent) {
int32 i;
int32 r, g, b, dr, dg, db;
RGB8 color, targetColor;
if ((firstPalEntry < 0) || (lastPalEntry > 255) || (firstPalEntry > lastPalEntry)) {
// This should generate an error
term_message("*** palette index error");
return;
}
term_message("Color tint DAC to: %d %d %d, %d percent, range (%d - %d)",
tintColor->r, tintColor->g, tintColor->b, percent, firstPalEntry, lastPalEntry);
percent = DivSF16(percent << 16, 100 << 16); // convert percent to frac16 format
targetColor.r = tintColor->r;
targetColor.g = tintColor->g;
targetColor.b = tintColor->b;
term_message("Doing palette.....");
if (!transparent) {
for (i = firstPalEntry; i <= lastPalEntry; ++i) {
// Calculate deltas for RGB's and put them in frac16 format
dr = (targetColor.r - _G(master_palette)[i].r) << 16;
dg = (targetColor.g - _G(master_palette)[i].g) << 16;
db = (targetColor.b - _G(master_palette)[i].b) << 16;
// New = orig + (delta * percent)
r = _G(master_palette)[i].r + (MulSF16(percent, dr) >> 16);
g = _G(master_palette)[i].g + (MulSF16(percent, dg) >> 16);
b = _G(master_palette)[i].b + (MulSF16(percent, db) >> 16);
// Check for under/overflow
r = CLIP(r, int32(0), int32(255));
g = CLIP(g, int32(0), int32(255));
b = CLIP(b, int32(0), int32(255));
color.r = (byte)r;
color.g = (byte)g;
color.b = (byte)b;
gr_pal_set_entry(i, &color); // Set the new color to the DAC
}
} else {
// This is for filtering colors. For example, a completely red filter
// (255, 0, 0) will block out the blue and green parts of the palette.
// 50% of the same filter will block out only 50% of the blue and
// green, but leaving all of the rest blue.
for (i = firstPalEntry; i <= lastPalEntry; ++i) {
// Converting rgb to a frac16 ( << 16) dividing by 256 ( >> 8)
// (the range of the palette values)
const int32 percent_r = (targetColor.r) << 8;
const int32 percent_g = (targetColor.g) << 8;
const int32 percent_b = (targetColor.b) << 8;
// This is the difference between the color and the full effect
// of the filter at 100%, as a frac16.
dr = (_G(master_palette)[i].r << 16) - (MulSF16(percent_r, _G(master_palette)[i].r << 16));
dg = (_G(master_palette)[i].g << 16) - (MulSF16(percent_g, _G(master_palette)[i].g << 16));
db = (_G(master_palette)[i].b << 16) - (MulSF16(percent_b, _G(master_palette)[i].b << 16));
// Scaling the effect to the right percentage. This is a frac16.
dr = MulSF16(dr, percent);
dg = MulSF16(dg, percent);
db = MulSF16(db, percent);
// Subtract the result to palette.
r = (_G(master_palette)[i].r - (dr >> 16));
g = (_G(master_palette)[i].g - (dg >> 16));
b = (_G(master_palette)[i].b - (db >> 16));
// Check for under/overflow
r = CLIP(r, int32(0), int32(255));
g = CLIP(g, int32(0), int32(255));
b = CLIP(b, int32(0), int32(255));
color.r = (byte)r;
color.g = (byte)g;
color.b = (byte)b;
gr_pal_set_entry(i, &color); // Set new colors to DAC.
}
}
}
void DAC_restore() {
term_message("DAC restored");
gr_pal_set_range(&_G(master_palette)[0], 0, 256);
}
} // namespace M4

View File

@@ -0,0 +1,142 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_KRN_PAL_H
#define M4_GRAPHICS_KRN_PAL_H
#include "m4/m4_types.h"
#include "m4/adv_r/adv_hotspot.h"
#include "m4/wscript/ws_machine.h"
namespace M4 {
#define TO_GREY (int32)0
#define TO_COLOR (int32)1
#define TO_BLACK (int32)2
struct KernelPal_Globals {
RGB8 _myFXPalette[256];
bool _myCycleReq = false;
bool _myCycleFinished = true;
bool _myCycleDACrefresh = false;
int32 _myCycleDelayTicks = 6; // 10 times a second
int32 _myCycleStartTime = 0;
int32 _myCycleEndTime = 0;
int32 _myCycleEndDelayTime = 0;
int32 _myCycleStartIndex = 0;
int32 _myCycleEndIndex = 0;
int32 _myCycleTrigger = 0;
int32 _myCycleNeverStopCycling = false;
bool _myFadeReq = false;
bool _myFadeFinished = true;
bool _myFadeDACrefresh = false;
int32 _myFadeDelayTicks = 3; // 20 times a second
int32 _myFadeStartTime = 0;
int32 _myFadeEndTime = 0;
int32 _myFadeEndDelayTime = 0;
int32 _myFadeStartIndex = 0;
int32 _myFadeEndIndex = 0;
int32 _myFadeTrigger = 0;
frac16 _myFadeStartPercentFrac = 0x10000;
frac16 _myFadeCurrPercentFrac = 0x10000;
frac16 _myFadePercentFrac = 0;
HotSpotRec *_exam_saved_hotspots = nullptr;
RGB8 *_fadeToMe = nullptr;
RGB8 *_trick = nullptr;
RGB8 *_picPal = nullptr;
int32 _seriesHash = 0;
machine *_seriesAnim8 = nullptr;
uint8 _translation[64];
int32 _colorAreaX1 = -1;
int32 _colorAreaY1 = -1;
int32 _colorAreaX2 = -1;
int32 _colorAreaY2 = -1;
int32 _greyAreaX1 = -1;
int32 _greyAreaY1 = -1;
int32 _greyAreaX2 = -1;
int32 _greyAreaY2 = -1;
bool _greyVideoMode = false;
};
void pal_fade_set_start(RGB8 *origPalette, int32 percent);
void pal_fade_set_start(int32 percent);
void pal_fade_init(RGB8 *origPalette, int32 firstPalEntry, int32 lastPalEntry, int32 targetPercent, int32 numTicks, int32 triggerNum);
void pal_fade_init(int32 firstPalEntry, int32 lastPalEntry, int32 targetPercent, int32 numTicks, int32 triggerNum);
void disable_player_commands_and_fade_init(int trigger);
void pal_cycle_init(int32 firstPalEntry, int32 lastPalEntry, int32 delayTicks,
int32 totalTicks = -1, int32 triggerNum = -1);
/**
* Returns true if color cycling is on
*/
bool pal_cycle_active();
/**
* Stops color cycling
*/
void pal_cycle_stop();
/**
* Starts color cycling
*/
void pal_cycle_resume();
/**
* Handles fading and cycling
*/
void pal_fx_update();
/**
* This is used to effect the screen colours (not the master palette) temporarily
* until something else updates the DAC e.g. refresh_DAC()
*/
void DAC_tint_range(const RGB8 *tintColor, int32 percent, int32 firstPalEntry, int32 lastPalEntry, bool transparent);
void kernel_examine_inventory_object(const char *picName, RGB8 *pal,
int steps, int delay, int32 x, int32 y, int32 trigger,
const char *digiName, int32 digiTrigger);
void kernel_examine_inventory_object(const char *picName, int steps, int delay,
int32 x, int32 y, int32 triggerNum, const char *digiName = nullptr, int32 digiTrigger = -1);
void kernel_unexamine_inventory_object(RGB8 *pal, int steps, int delay);
void remap_buffer_with_luminance_map(Buffer *src, int32 x1, int32 y1, int32 x2, int32 y2);
void krn_SetGreyVideoMode(int32 grey_x1, int32 grey_y1, int32 grey_x2, int32 grey_y2, int32 color_x1, int32 color_y1, int32 color_x2, int32 color_y2);
void krn_UnsetGreyVideoMode(void);
bool krn_GetGreyMode(void);
void krn_UpdateGreyArea(Buffer *greyOutThisBuffer, int32 scrnX, int32 scrnY,
int32 greyX1, int32 greyY1, int32 greyX2, int32 greyY2);
void krn_ChangeBufferLuminance(Buffer *target, int32 percent);
void krn_pal_game_task();
void krn_fade_from_grey(RGB8 *pal, int32 steps, int32 delay, int32 fadeType);
void krn_fade_to_grey(RGB8 *pal, int32 steps, int32 delay);
} // namespace M4
#endif

1028
engines/m4/graphics/rend.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
/* 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 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/>.
*
*/
#ifndef M4_GRAPHICS_REND_H
#define M4_GRAPHICS_REND_H
#include "m4/m4_types.h"
#include "m4/gui/gui.h"
namespace M4 {
struct RGBcolor {
uint8 b, g, r;
};
struct RendGrBuff {
uint32 Width = 0;
uint32 Height = 0;
void *PixMap = nullptr;
byte encoding = 0;
uint32 Pitch = 0;
};
struct DrawRequestX {
int32 x; // X position relative to GrBuff(0, 0)
int32 y; // Y position relative to GrBuff(0, 0)
int32 scale_x; // X scale factor (can be negative for reverse draw)
int32 scale_y; // Y scale factor (can't be negative)
uint8 *depth_map; // Depth code array for destination (doesn't care if srcDepth is 0)
RGBcolor *Pal; // Palette for shadow draw (doesn't care if SHADOW bit is not set in Src.encoding)
uint8 *ICT; // Inverse Color Table (doesn't care if SHADOW bit is not set in Src.encoding)
uint8 depth; // Depth code for source (0 if no depth processing)
};
struct RendCell {
uint32 Pack;
uint32 Stream;
long hot_x;
long hot_y;
uint32 Width;
uint32 Height;
uint32 Comp;
uint32 Reserved[8];
uint8 *data;
};
enum {
kEndOfLine = 0,
kEndOfSprite = 1,
kJumpXY = 2
};
typedef uint32 RenderResult;
typedef RenderResult(*RenderFunc)();
struct Rend_Globals {
uint8 *_sourceAddress, *_destinationAddress, *_depthAddress, _spriteDepth, *_InverseColorTable;
int32 _X_scale, _LeftPorch, _RightPorch, _StartingPixelPos, _X_error;
int _Increment;
RGBcolor *_Palette;
};
void GetUpdateRectangle(int32 x, int32 y, int32 hot_x, int32 hot_y, int32 scale_x, int32 scale_y, int32 Width, int32 Height, M4Rect *UpdateRect);
void render_sprite_to_8BBM(RendGrBuff *Destination, DrawRequestX *dr, RendCell *Frame, M4Rect *ClipRectangle, M4Rect *UpdateRect);
} // namespace M4
#endif