mirror of https://github.com/tp4a/teleport
.temp. new rdp-player.
parent
f5b00ca02a
commit
0b020810b2
|
@ -0,0 +1,15 @@
|
|||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
//#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
//#endif
|
||||
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "rle.h"
|
||||
|
||||
#include <QMatrix>
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
|
||||
bool rdpimg2QImage(QImage& out, int w, int h, int bitsPerPixel, bool isCompressed, uint8_t* dat, uint32_t len) {
|
||||
switch(bitsPerPixel) {
|
||||
case 15:
|
||||
if(isCompressed) {
|
||||
uint8_t* _dat = (uint8_t*)calloc(1, w*h*2);
|
||||
if(!bitmap_decompress1(_dat, w, h, dat, len)) {
|
||||
free(_dat);
|
||||
return false;
|
||||
}
|
||||
out = QImage(_dat, w, h, QImage::Format_RGB555);
|
||||
free(_dat);
|
||||
}
|
||||
else {
|
||||
out = QImage(dat, w, h, QImage::Format_RGB555).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) ;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
if(isCompressed) {
|
||||
uint8_t* _dat = (uint8_t*)calloc(1, w*h*2);
|
||||
if(!bitmap_decompress2(_dat, w, h, dat, len)) {
|
||||
free(_dat);
|
||||
return false;
|
||||
}
|
||||
out = QImage(_dat, w, h, QImage::Format_RGB16);
|
||||
free(_dat);
|
||||
}
|
||||
else {
|
||||
out = QImage(dat, w, h, QImage::Format_RGB16).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) ;
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
qDebug() << "--------NOT support 24";
|
||||
break;
|
||||
case 32:
|
||||
qDebug() << "--------NOT support 32";
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
m_shown = false;
|
||||
m_show_bg = true;
|
||||
m_bg = QImage(":/tp-player/res/bg");
|
||||
m_pt_normal = QImage(":/tp-player/res/cursor.png");
|
||||
|
||||
qDebug() << m_pt_normal.width() << "x" << m_pt_normal.height();
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
//qRegisterMetaType<update_data*>("update_data");
|
||||
|
||||
// frame-less window.
|
||||
//#ifdef __APPLE__
|
||||
// setWindowFlags(Qt::FramelessWindowHint | Qt::MSWindowsFixedSizeDialogHint | Qt::Window);
|
||||
// OSXCode::fixWin(winId());
|
||||
//#else
|
||||
// setWindowFlags(Qt::FramelessWindowHint | Qt::MSWindowsFixedSizeDialogHint | windowFlags());
|
||||
//#endif //__APPLE__
|
||||
|
||||
setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint); // 禁止最大化按钮
|
||||
//setFixedSize(this->width(),this->height()); // 禁止拖动窗口大小
|
||||
|
||||
resize(m_bg.width(), m_bg.height());
|
||||
|
||||
connect(&m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(on_update_data(update_data*)));
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
m_thr_play.stop();
|
||||
m_thr_play.wait();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
if(m_show_bg) {
|
||||
//qDebug() << "draw bg.";
|
||||
painter.setBrush(Qt::black);
|
||||
painter.drawRect(this->rect());
|
||||
|
||||
int x = (rect().width() - m_bg.width()) / 2;
|
||||
int y = (rect().height() - m_bg.height()) / 2;
|
||||
painter.drawImage(x, y, m_bg);
|
||||
//painter.drawPixmap(rect(), m_bg1);
|
||||
}
|
||||
|
||||
else {
|
||||
painter.drawImage(m_img_update_x, m_img_update_y, m_img_update, 0, 0, m_img_update_w, m_img_update_h, Qt::AutoColor);
|
||||
//qDebug() << "draw pt (" << m_pt.x << "," << m_pt.y << ")";
|
||||
painter.drawImage(m_pt.x, m_pt.y, m_pt_normal);
|
||||
}
|
||||
|
||||
|
||||
if(!m_shown) {
|
||||
m_shown = true;
|
||||
m_thr_play.start();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_update_data(update_data* dat) {
|
||||
if(!dat)
|
||||
return;
|
||||
// qDebug() << "slot-event: " << dat->data_type();
|
||||
|
||||
if(dat->data_type() == TYPE_DATA) {
|
||||
m_show_bg = false;
|
||||
|
||||
if(dat->data_len() <= sizeof(TS_RECORD_PKG)) {
|
||||
qDebug() << "invalid record package(1).";
|
||||
delete dat;
|
||||
return;
|
||||
}
|
||||
|
||||
TS_RECORD_PKG* pkg = (TS_RECORD_PKG*)dat->data_buf();
|
||||
|
||||
if(pkg->type == TS_RECORD_TYPE_RDP_POINTER) {
|
||||
if(dat->data_len() != sizeof(TS_RECORD_PKG) + sizeof(TS_RECORD_RDP_POINTER)) {
|
||||
qDebug() << "invalid record package(2).";
|
||||
delete dat;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&m_pt, dat->data_buf() + sizeof(TS_RECORD_PKG), sizeof(TS_RECORD_RDP_POINTER));
|
||||
update();
|
||||
//update(m_pt.x - 8, m_pt.y - 8, 32, 32);
|
||||
}
|
||||
else if(pkg->type == TS_RECORD_TYPE_RDP_IMAGE) {
|
||||
if(dat->data_len() <= sizeof(TS_RECORD_PKG) + sizeof(TS_RECORD_RDP_IMAGE_INFO)) {
|
||||
qDebug() << "invalid record package(3).";
|
||||
delete dat;
|
||||
return;
|
||||
}
|
||||
|
||||
TS_RECORD_RDP_IMAGE_INFO* info = (TS_RECORD_RDP_IMAGE_INFO*)(dat->data_buf() + sizeof(TS_RECORD_PKG));
|
||||
uint8_t* img_dat = dat->data_buf() + sizeof(TS_RECORD_PKG) + sizeof(TS_RECORD_RDP_IMAGE_INFO);
|
||||
uint32_t img_len = dat->data_len() - sizeof(TS_RECORD_PKG) - sizeof(TS_RECORD_RDP_IMAGE_INFO);
|
||||
|
||||
rdpimg2QImage(m_img_update, info->width, info->height, info->bitsPerPixel, (info->format == TS_RDP_IMG_BMP) ? true : false, img_dat, img_len);
|
||||
m_img_update_x = info->destLeft;
|
||||
m_img_update_y = info->destTop;
|
||||
m_img_update_w = info->destRight - info->destLeft + 1;
|
||||
m_img_update_h = info->destBottom - info->destTop + 1;
|
||||
|
||||
qDebug() << "img " << ((info->format == TS_RDP_IMG_BMP) ? "+" : " ") << " (" << m_img_update_x << "," << m_img_update_y << "), [" << m_img_update.width() << "x" << m_img_update.height() << "]";
|
||||
|
||||
|
||||
update(m_img_update_x, m_img_update_y, m_img_update_w, m_img_update_h);
|
||||
}
|
||||
|
||||
delete dat;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(dat->data_type() == TYPE_HEADER_INFO) {
|
||||
if(dat->data_len() != sizeof(TS_RECORD_HEADER)) {
|
||||
qDebug() << "invalid record header.";
|
||||
delete dat;
|
||||
return;
|
||||
}
|
||||
memcpy(&m_rec_hdr, dat->data_buf(), sizeof(TS_RECORD_HEADER));
|
||||
delete dat;
|
||||
|
||||
qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")";
|
||||
if(m_rec_hdr.basic.width > 0 && m_rec_hdr.basic.height > 0) {
|
||||
resize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
}
|
||||
|
||||
QString title;
|
||||
if (m_rec_hdr.basic.conn_port == 3389)
|
||||
title.sprintf("[%s] %s@%s [Teleport-RDP录像回放]", m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip);
|
||||
else
|
||||
title.sprintf("[%s] %s@%s:%d [Teleport-RDP录像回放]", m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, m_rec_hdr.basic.conn_port);
|
||||
|
||||
setWindowTitle(title);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
delete dat;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "thr_play.h"
|
||||
#include "update_data.h"
|
||||
#include "record_format.h"
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *);
|
||||
|
||||
|
||||
private slots:
|
||||
void on_update_data(update_data*);
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
QImage m_bg;
|
||||
QImage m_pt_normal;
|
||||
QImage m_img_update;
|
||||
//QPixmap m_bg1;
|
||||
bool m_shown;
|
||||
|
||||
ThreadPlay m_thr_play;
|
||||
|
||||
bool m_show_bg;
|
||||
TS_RECORD_HEADER m_rec_hdr;
|
||||
TS_RECORD_RDP_POINTER m_pt;
|
||||
|
||||
int m_img_update_x;
|
||||
int m_img_update_y;
|
||||
int m_img_update_w;
|
||||
int m_img_update_h;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>360</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Teleport Replayer</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget"/>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>17</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar">
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,96 @@
|
|||
#ifndef RECORD_FORMAT_H
|
||||
#define RECORD_FORMAT_H
|
||||
|
||||
#include <Qt>
|
||||
|
||||
|
||||
#define TYPE_HEADER_INFO 0
|
||||
#define TYPE_DATA 1
|
||||
|
||||
|
||||
#define TS_RECORD_TYPE_RDP_POINTER 0x12 // 鼠标坐标位置改变,用于绘制虚拟鼠标
|
||||
#define TS_RECORD_TYPE_RDP_IMAGE 0x13 // 服务端返回的图像,用于展示
|
||||
|
||||
#define TS_RDP_BTN_FREE 0
|
||||
#define TS_RDP_BTN_PRESSED 1
|
||||
#define TS_RDP_IMG_RAW 0 // 未压缩,原始数据(根据bitsPerPixel,多个字节对应一个点的颜色)
|
||||
#define TS_RDP_IMG_BMP 1 // 压缩的BMP数据
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
// 录像文件头(随着录像数据写入,会改变的部分)
|
||||
typedef struct TS_RECORD_HEADER_INFO {
|
||||
uint32_t magic; // "TPPR" 标志 TelePort Protocol Record
|
||||
uint16_t ver; // 录像文件版本,目前为3
|
||||
uint32_t packages; // 总包数
|
||||
uint32_t time_ms; // 总耗时(毫秒)
|
||||
//uint32_t file_size; // 数据文件大小
|
||||
}TS_RECORD_HEADER_INFO;
|
||||
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
|
||||
|
||||
// 录像文件头(固定不变部分)
|
||||
typedef struct TS_RECORD_HEADER_BASIC {
|
||||
uint16_t protocol_type; // 协议:1=RDP, 2=SSH, 3=Telnet
|
||||
uint16_t protocol_sub_type; // 子协议:100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
|
||||
uint64_t timestamp; // 本次录像的起始时间(UTC时间戳)
|
||||
uint16_t width; // 初始屏幕尺寸:宽
|
||||
uint16_t height; // 初始屏幕尺寸:高
|
||||
char user_username[64]; // teleport账号
|
||||
char acc_username[64]; // 远程主机用户名
|
||||
|
||||
char host_ip[40]; // 远程主机IP
|
||||
char conn_ip[40]; // 远程主机IP
|
||||
uint16_t conn_port; // 远程主机端口
|
||||
|
||||
char client_ip[40]; // 客户端IP
|
||||
|
||||
// // RDP专有
|
||||
// uint8_t rdp_security; // 0 = RDP, 1 = TLS
|
||||
|
||||
// uint8_t _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - 1 - ts_record_header_info_size];
|
||||
uint8_t _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - ts_record_header_info_size];
|
||||
}TS_RECORD_HEADER_BASIC;
|
||||
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
|
||||
|
||||
typedef struct TS_RECORD_HEADER {
|
||||
TS_RECORD_HEADER_INFO info;
|
||||
TS_RECORD_HEADER_BASIC basic;
|
||||
}TS_RECORD_HEADER;
|
||||
|
||||
// header部分(header-info + header-basic) = 512B
|
||||
#define ts_record_header_size sizeof(TS_RECORD_HEADER)
|
||||
|
||||
// 一个数据包的头
|
||||
typedef struct TS_RECORD_PKG {
|
||||
uint8_t type; // 包的数据类型
|
||||
uint32_t size; // 这个包的总大小(不含包头)
|
||||
uint32_t time_ms; // 这个包距起始时间的时间差(毫秒,意味着一个连接不能持续超过49天)
|
||||
uint8_t _reserve[3]; // 保留
|
||||
}TS_RECORD_PKG;
|
||||
|
||||
|
||||
typedef struct TS_RECORD_RDP_POINTER {
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
uint8_t button;
|
||||
uint8_t pressed;
|
||||
}TS_RECORD_RDP_POINTER;
|
||||
|
||||
// RDP图像更新
|
||||
typedef struct TS_RECORD_RDP_IMAGE_INFO {
|
||||
uint16_t destLeft;
|
||||
uint16_t destTop;
|
||||
uint16_t destRight;
|
||||
uint16_t destBottom;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t bitsPerPixel;
|
||||
uint8_t format;
|
||||
uint8_t _reserved;
|
||||
}TS_RECORD_RDP_IMAGE_INFO;
|
||||
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
#endif // RECORD_FORMAT_H
|
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,974 @@
|
|||
/* -*- c-basic-offset: 8 -*-
|
||||
rdesktop: A Remote Desktop Protocol client.
|
||||
Bitmap decompression routines
|
||||
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
/* three seperate function for speed when decompressing the bitmaps
|
||||
when modifing one function make the change in the others
|
||||
jay.sorg@gmail.com */
|
||||
|
||||
/* indent is confused by this file */
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "rle.h"
|
||||
|
||||
/* Specific rename for RDPY integration */
|
||||
#define unimpl(str, code)
|
||||
|
||||
//#define RD_BOOL int
|
||||
//#define False 0
|
||||
//#define True 1
|
||||
/* end specific rename */
|
||||
|
||||
#define CVAL(p) (*(p++))
|
||||
//#ifdef NEED_ALIGN
|
||||
//#ifdef L_ENDIAN
|
||||
#define CVAL2(p, v) { v = (*(p++)); v |= (*(p++)) << 8; }
|
||||
//#else
|
||||
//#define CVAL2(p, v) { v = (*(p++)) << 8; v |= (*(p++)); }
|
||||
//#endif /* L_ENDIAN */
|
||||
//#else
|
||||
//#define CVAL2(p, v) { v = (*((uint16*)p)); p += 2; }
|
||||
//#endif /* NEED_ALIGN */
|
||||
|
||||
#define UNROLL8(exp) { exp exp exp exp exp exp exp exp }
|
||||
|
||||
#define REPEAT(statement) \
|
||||
{ \
|
||||
while((count & ~0x7) && ((x+8) < width)) \
|
||||
UNROLL8( statement; count--; x++; ); \
|
||||
\
|
||||
while((count > 0) && (x < width)) \
|
||||
{ \
|
||||
statement; \
|
||||
count--; \
|
||||
x++; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define MASK_UPDATE() \
|
||||
{ \
|
||||
mixmask <<= 1; \
|
||||
if (mixmask == 0) \
|
||||
{ \
|
||||
mask = fom_mask ? fom_mask : CVAL(input); \
|
||||
mixmask = 1; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* 1 byte bitmap decompress */
|
||||
RD_BOOL
|
||||
bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size)
|
||||
{
|
||||
uint8 *end = input + size;
|
||||
uint8 *prevline = NULL, *line = NULL;
|
||||
int opcode, count, offset, isfillormix, x = width;
|
||||
int lastopcode = -1, insertmix = False, bicolour = False;
|
||||
uint8 code;
|
||||
uint8 colour1 = 0, colour2 = 0;
|
||||
uint8 mixmask, mask = 0;
|
||||
uint8 mix = 0xff;
|
||||
int fom_mask = 0;
|
||||
|
||||
while (input < end)
|
||||
{
|
||||
fom_mask = 0;
|
||||
code = CVAL(input);
|
||||
opcode = code >> 4;
|
||||
/* Handle different opcode forms */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
opcode -= 6;
|
||||
count = code & 0xf;
|
||||
offset = 16;
|
||||
break;
|
||||
case 0xf:
|
||||
opcode = code & 0xf;
|
||||
if (opcode < 9)
|
||||
{
|
||||
count = CVAL(input);
|
||||
count |= CVAL(input) << 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (opcode < 0xb) ? 8 : 1;
|
||||
}
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
opcode >>= 1;
|
||||
count = code & 0x1f;
|
||||
offset = 32;
|
||||
break;
|
||||
}
|
||||
/* Handle strange cases for counts */
|
||||
if (offset != 0)
|
||||
{
|
||||
isfillormix = ((opcode == 2) || (opcode == 7));
|
||||
if (count == 0)
|
||||
{
|
||||
if (isfillormix)
|
||||
count = CVAL(input) + 1;
|
||||
else
|
||||
count = CVAL(input) + offset;
|
||||
}
|
||||
else if (isfillormix)
|
||||
{
|
||||
count <<= 3;
|
||||
}
|
||||
}
|
||||
/* Read preliminary data */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
|
||||
insertmix = True;
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
colour1 = CVAL(input);
|
||||
case 3: /* Colour */
|
||||
colour2 = CVAL(input);
|
||||
break;
|
||||
case 6: /* SetMix/Mix */
|
||||
case 7: /* SetMix/FillOrMix */
|
||||
mix = CVAL(input);
|
||||
opcode -= 5;
|
||||
break;
|
||||
case 9: /* FillOrMix_1 */
|
||||
mask = 0x03;
|
||||
opcode = 0x02;
|
||||
fom_mask = 3;
|
||||
break;
|
||||
case 0x0a: /* FillOrMix_2 */
|
||||
mask = 0x05;
|
||||
opcode = 0x02;
|
||||
fom_mask = 5;
|
||||
break;
|
||||
}
|
||||
lastopcode = opcode;
|
||||
mixmask = 0;
|
||||
/* Output body */
|
||||
while (count > 0)
|
||||
{
|
||||
if (x >= width)
|
||||
{
|
||||
if (height <= 0)
|
||||
return False;
|
||||
x = 0;
|
||||
height--;
|
||||
prevline = line;
|
||||
line = output + height * width;
|
||||
}
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if (insertmix)
|
||||
{
|
||||
if (prevline == NULL)
|
||||
line[x] = mix;
|
||||
else
|
||||
line[x] = prevline[x] ^ mix;
|
||||
insertmix = False;
|
||||
count--;
|
||||
x++;
|
||||
}
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT(line[x] = 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT(line[x] = prevline[x])
|
||||
}
|
||||
break;
|
||||
case 1: /* Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT(line[x] = mix)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT(line[x] = prevline[x] ^ mix)
|
||||
}
|
||||
break;
|
||||
case 2: /* Fill or Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
line[x] = mix;
|
||||
else
|
||||
line[x] = 0;
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
line[x] = prevline[x] ^ mix;
|
||||
else
|
||||
line[x] = prevline[x];
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 3: /* Colour */
|
||||
REPEAT(line[x] = colour2)
|
||||
break;
|
||||
case 4: /* Copy */
|
||||
REPEAT(line[x] = CVAL(input))
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
REPEAT
|
||||
(
|
||||
if (bicolour)
|
||||
{
|
||||
line[x] = colour2;
|
||||
bicolour = False;
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x] = colour1;
|
||||
bicolour = True; count++;
|
||||
}
|
||||
)
|
||||
break;
|
||||
case 0xd: /* White */
|
||||
REPEAT(line[x] = 0xff)
|
||||
break;
|
||||
case 0xe: /* Black */
|
||||
REPEAT(line[x] = 0)
|
||||
break;
|
||||
default:
|
||||
unimpl("bitmap opcode 0x%x\n", opcode);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/* 2 byte bitmap decompress */
|
||||
RD_BOOL
|
||||
bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int size)
|
||||
{
|
||||
uint8 *end = input + size;
|
||||
uint16 *prevline = NULL, *line = NULL;
|
||||
int opcode, count, offset, isfillormix, x = width;
|
||||
int lastopcode = -1, insertmix = False, bicolour = False;
|
||||
uint8 code;
|
||||
uint16 colour1 = 0, colour2 = 0;
|
||||
uint8 mixmask, mask = 0;
|
||||
uint16 mix = 0xffff;
|
||||
int fom_mask = 0;
|
||||
|
||||
while (input < end)
|
||||
{
|
||||
fom_mask = 0;
|
||||
code = CVAL(input);
|
||||
opcode = code >> 4;
|
||||
/* Handle different opcode forms */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
opcode -= 6;
|
||||
count = code & 0xf;
|
||||
offset = 16;
|
||||
break;
|
||||
case 0xf:
|
||||
opcode = code & 0xf;
|
||||
if (opcode < 9)
|
||||
{
|
||||
count = CVAL(input);
|
||||
count |= CVAL(input) << 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (opcode < 0xb) ? 8 : 1;
|
||||
}
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
opcode >>= 1;
|
||||
count = code & 0x1f;
|
||||
offset = 32;
|
||||
break;
|
||||
}
|
||||
/* Handle strange cases for counts */
|
||||
if (offset != 0)
|
||||
{
|
||||
isfillormix = ((opcode == 2) || (opcode == 7));
|
||||
if (count == 0)
|
||||
{
|
||||
if (isfillormix)
|
||||
count = CVAL(input) + 1;
|
||||
else
|
||||
count = CVAL(input) + offset;
|
||||
}
|
||||
else if (isfillormix)
|
||||
{
|
||||
count <<= 3;
|
||||
}
|
||||
}
|
||||
/* Read preliminary data */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
|
||||
insertmix = True;
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
CVAL2(input, colour1);
|
||||
case 3: /* Colour */
|
||||
CVAL2(input, colour2);
|
||||
break;
|
||||
case 6: /* SetMix/Mix */
|
||||
case 7: /* SetMix/FillOrMix */
|
||||
CVAL2(input, mix);
|
||||
opcode -= 5;
|
||||
break;
|
||||
case 9: /* FillOrMix_1 */
|
||||
mask = 0x03;
|
||||
opcode = 0x02;
|
||||
fom_mask = 3;
|
||||
break;
|
||||
case 0x0a: /* FillOrMix_2 */
|
||||
mask = 0x05;
|
||||
opcode = 0x02;
|
||||
fom_mask = 5;
|
||||
break;
|
||||
}
|
||||
lastopcode = opcode;
|
||||
mixmask = 0;
|
||||
/* Output body */
|
||||
while (count > 0)
|
||||
{
|
||||
if (x >= width)
|
||||
{
|
||||
if (height <= 0)
|
||||
return False;
|
||||
x = 0;
|
||||
height--;
|
||||
prevline = line;
|
||||
line = ((uint16 *) output) + height * width;
|
||||
}
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if (insertmix)
|
||||
{
|
||||
if (prevline == NULL)
|
||||
line[x] = mix;
|
||||
else
|
||||
line[x] = prevline[x] ^ mix;
|
||||
insertmix = False;
|
||||
count--;
|
||||
x++;
|
||||
}
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT(line[x] = 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT(line[x] = prevline[x])
|
||||
}
|
||||
break;
|
||||
case 1: /* Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT(line[x] = mix)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT(line[x] = prevline[x] ^ mix)
|
||||
}
|
||||
break;
|
||||
case 2: /* Fill or Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
line[x] = mix;
|
||||
else
|
||||
line[x] = 0;
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
line[x] = prevline[x] ^ mix;
|
||||
else
|
||||
line[x] = prevline[x];
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 3: /* Colour */
|
||||
REPEAT(line[x] = colour2)
|
||||
break;
|
||||
case 4: /* Copy */
|
||||
REPEAT(CVAL2(input, line[x]))
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
REPEAT
|
||||
(
|
||||
if (bicolour)
|
||||
{
|
||||
line[x] = colour2;
|
||||
bicolour = False;
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x] = colour1;
|
||||
bicolour = True;
|
||||
count++;
|
||||
}
|
||||
)
|
||||
break;
|
||||
case 0xd: /* White */
|
||||
REPEAT(line[x] = 0xffff)
|
||||
break;
|
||||
case 0xe: /* Black */
|
||||
REPEAT(line[x] = 0)
|
||||
break;
|
||||
default:
|
||||
unimpl("bitmap opcode 0x%x\n", opcode);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/* 3 byte bitmap decompress */
|
||||
RD_BOOL
|
||||
bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size)
|
||||
{
|
||||
uint8 *end = input + size;
|
||||
uint8 *prevline = NULL, *line = NULL;
|
||||
int opcode, count, offset, isfillormix, x = width;
|
||||
int lastopcode = -1, insertmix = False, bicolour = False;
|
||||
uint8 code;
|
||||
uint8 colour1[3] = {0, 0, 0}, colour2[3] = {0, 0, 0};
|
||||
uint8 mixmask, mask = 0;
|
||||
uint8 mix[3] = {0xff, 0xff, 0xff};
|
||||
int fom_mask = 0;
|
||||
|
||||
while (input < end)
|
||||
{
|
||||
fom_mask = 0;
|
||||
code = CVAL(input);
|
||||
opcode = code >> 4;
|
||||
/* Handle different opcode forms */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
opcode -= 6;
|
||||
count = code & 0xf;
|
||||
offset = 16;
|
||||
break;
|
||||
case 0xf:
|
||||
opcode = code & 0xf;
|
||||
if (opcode < 9)
|
||||
{
|
||||
count = CVAL(input);
|
||||
count |= CVAL(input) << 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (opcode <
|
||||
0xb) ? 8 : 1;
|
||||
}
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
opcode >>= 1;
|
||||
count = code & 0x1f;
|
||||
offset = 32;
|
||||
break;
|
||||
}
|
||||
/* Handle strange cases for counts */
|
||||
if (offset != 0)
|
||||
{
|
||||
isfillormix = ((opcode == 2) || (opcode == 7));
|
||||
if (count == 0)
|
||||
{
|
||||
if (isfillormix)
|
||||
count = CVAL(input) + 1;
|
||||
else
|
||||
count = CVAL(input) + offset;
|
||||
}
|
||||
else if (isfillormix)
|
||||
{
|
||||
count <<= 3;
|
||||
}
|
||||
}
|
||||
/* Read preliminary data */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
|
||||
insertmix = True;
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
colour1[0] = CVAL(input);
|
||||
colour1[1] = CVAL(input);
|
||||
colour1[2] = CVAL(input);
|
||||
case 3: /* Colour */
|
||||
colour2[0] = CVAL(input);
|
||||
colour2[1] = CVAL(input);
|
||||
colour2[2] = CVAL(input);
|
||||
break;
|
||||
case 6: /* SetMix/Mix */
|
||||
case 7: /* SetMix/FillOrMix */
|
||||
mix[0] = CVAL(input);
|
||||
mix[1] = CVAL(input);
|
||||
mix[2] = CVAL(input);
|
||||
opcode -= 5;
|
||||
break;
|
||||
case 9: /* FillOrMix_1 */
|
||||
mask = 0x03;
|
||||
opcode = 0x02;
|
||||
fom_mask = 3;
|
||||
break;
|
||||
case 0x0a: /* FillOrMix_2 */
|
||||
mask = 0x05;
|
||||
opcode = 0x02;
|
||||
fom_mask = 5;
|
||||
break;
|
||||
}
|
||||
lastopcode = opcode;
|
||||
mixmask = 0;
|
||||
/* Output body */
|
||||
while (count > 0)
|
||||
{
|
||||
if (x >= width)
|
||||
{
|
||||
if (height <= 0)
|
||||
return False;
|
||||
x = 0;
|
||||
height--;
|
||||
prevline = line;
|
||||
line = output + height * (width * 3);
|
||||
}
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if (insertmix)
|
||||
{
|
||||
if (prevline == NULL)
|
||||
{
|
||||
line[x * 3] = mix[0];
|
||||
line[x * 3 + 1] = mix[1];
|
||||
line[x * 3 + 2] = mix[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x * 3] =
|
||||
prevline[x * 3] ^ mix[0];
|
||||
line[x * 3 + 1] =
|
||||
prevline[x * 3 + 1] ^ mix[1];
|
||||
line[x * 3 + 2] =
|
||||
prevline[x * 3 + 2] ^ mix[2];
|
||||
}
|
||||
insertmix = False;
|
||||
count--;
|
||||
x++;
|
||||
}
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = 0;
|
||||
line[x * 3 + 1] = 0;
|
||||
line[x * 3 + 2] = 0;
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = prevline[x * 3];
|
||||
line[x * 3 + 1] = prevline[x * 3 + 1];
|
||||
line[x * 3 + 2] = prevline[x * 3 + 2];
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 1: /* Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = mix[0];
|
||||
line[x * 3 + 1] = mix[1];
|
||||
line[x * 3 + 2] = mix[2];
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] =
|
||||
prevline[x * 3] ^ mix[0];
|
||||
line[x * 3 + 1] =
|
||||
prevline[x * 3 + 1] ^ mix[1];
|
||||
line[x * 3 + 2] =
|
||||
prevline[x * 3 + 2] ^ mix[2];
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 2: /* Fill or Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
{
|
||||
line[x * 3] = mix[0];
|
||||
line[x * 3 + 1] = mix[1];
|
||||
line[x * 3 + 2] = mix[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x * 3] = 0;
|
||||
line[x * 3 + 1] = 0;
|
||||
line[x * 3 + 2] = 0;
|
||||
}
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
{
|
||||
line[x * 3] =
|
||||
prevline[x * 3] ^ mix [0];
|
||||
line[x * 3 + 1] =
|
||||
prevline[x * 3 + 1] ^ mix [1];
|
||||
line[x * 3 + 2] =
|
||||
prevline[x * 3 + 2] ^ mix [2];
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x * 3] =
|
||||
prevline[x * 3];
|
||||
line[x * 3 + 1] =
|
||||
prevline[x * 3 + 1];
|
||||
line[x * 3 + 2] =
|
||||
prevline[x * 3 + 2];
|
||||
}
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 3: /* Colour */
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = colour2 [0];
|
||||
line[x * 3 + 1] = colour2 [1];
|
||||
line[x * 3 + 2] = colour2 [2];
|
||||
)
|
||||
break;
|
||||
case 4: /* Copy */
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = CVAL(input);
|
||||
line[x * 3 + 1] = CVAL(input);
|
||||
line[x * 3 + 2] = CVAL(input);
|
||||
)
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
REPEAT
|
||||
(
|
||||
if (bicolour)
|
||||
{
|
||||
line[x * 3] = colour2[0];
|
||||
line[x * 3 + 1] = colour2[1];
|
||||
line[x * 3 + 2] = colour2[2];
|
||||
bicolour = False;
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x * 3] = colour1[0];
|
||||
line[x * 3 + 1] = colour1[1];
|
||||
line[x * 3 + 2] = colour1[2];
|
||||
bicolour = True;
|
||||
count++;
|
||||
}
|
||||
)
|
||||
break;
|
||||
case 0xd: /* White */
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = 0xff;
|
||||
line[x * 3 + 1] = 0xff;
|
||||
line[x * 3 + 2] = 0xff;
|
||||
)
|
||||
break;
|
||||
case 0xe: /* Black */
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = 0;
|
||||
line[x * 3 + 1] = 0;
|
||||
line[x * 3 + 2] = 0;
|
||||
)
|
||||
break;
|
||||
default:
|
||||
unimpl("bitmap opcode 0x%x\n", opcode);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/* decompress a colour plane */
|
||||
static int
|
||||
process_plane(uint8 * in, int width, int height, uint8 * out, int size)
|
||||
{
|
||||
int indexw;
|
||||
int indexh;
|
||||
int code;
|
||||
int collen;
|
||||
int replen;
|
||||
int color;
|
||||
int x;
|
||||
int revcode;
|
||||
uint8 * last_line;
|
||||
uint8 * this_line;
|
||||
uint8 * org_in;
|
||||
uint8 * org_out;
|
||||
|
||||
org_in = in;
|
||||
org_out = out;
|
||||
last_line = 0;
|
||||
indexh = 0;
|
||||
while (indexh < height)
|
||||
{
|
||||
out = (org_out + width * height * 4) - ((indexh + 1) * width * 4);
|
||||
color = 0;
|
||||
this_line = out;
|
||||
indexw = 0;
|
||||
if (last_line == 0)
|
||||
{
|
||||
while (indexw < width)
|
||||
{
|
||||
code = CVAL(in);
|
||||
replen = code & 0xf;
|
||||
collen = (code >> 4) & 0xf;
|
||||
revcode = (replen << 4) | collen;
|
||||
if ((revcode <= 47) && (revcode >= 16))
|
||||
{
|
||||
replen = revcode;
|
||||
collen = 0;
|
||||
}
|
||||
while (collen > 0)
|
||||
{
|
||||
color = CVAL(in);
|
||||
*out = color;
|
||||
out += 4;
|
||||
indexw++;
|
||||
collen--;
|
||||
}
|
||||
while (replen > 0)
|
||||
{
|
||||
*out = color;
|
||||
out += 4;
|
||||
indexw++;
|
||||
replen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (indexw < width)
|
||||
{
|
||||
code = CVAL(in);
|
||||
replen = code & 0xf;
|
||||
collen = (code >> 4) & 0xf;
|
||||
revcode = (replen << 4) | collen;
|
||||
if ((revcode <= 47) && (revcode >= 16))
|
||||
{
|
||||
replen = revcode;
|
||||
collen = 0;
|
||||
}
|
||||
while (collen > 0)
|
||||
{
|
||||
x = CVAL(in);
|
||||
if (x & 1)
|
||||
{
|
||||
x = x >> 1;
|
||||
x = x + 1;
|
||||
color = -x;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = x >> 1;
|
||||
color = x;
|
||||
}
|
||||
x = last_line[indexw * 4] + color;
|
||||
*out = x;
|
||||
out += 4;
|
||||
indexw++;
|
||||
collen--;
|
||||
}
|
||||
while (replen > 0)
|
||||
{
|
||||
x = last_line[indexw * 4] + color;
|
||||
*out = x;
|
||||
out += 4;
|
||||
indexw++;
|
||||
replen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
indexh++;
|
||||
last_line = this_line;
|
||||
}
|
||||
return (int) (in - org_in);
|
||||
}
|
||||
|
||||
/* 4 byte bitmap decompress */
|
||||
RD_BOOL
|
||||
bitmap_decompress4(uint8 * output, int width, int height, uint8 * input, int size)
|
||||
{
|
||||
int code;
|
||||
int bytes_pro;
|
||||
int total_pro;
|
||||
|
||||
code = CVAL(input);
|
||||
if (code != 0x10)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
total_pro = 1;
|
||||
bytes_pro = process_plane(input, width, height, output + 3, size - total_pro);
|
||||
total_pro += bytes_pro;
|
||||
input += bytes_pro;
|
||||
bytes_pro = process_plane(input, width, height, output + 2, size - total_pro);
|
||||
total_pro += bytes_pro;
|
||||
input += bytes_pro;
|
||||
bytes_pro = process_plane(input, width, height, output + 1, size - total_pro);
|
||||
total_pro += bytes_pro;
|
||||
input += bytes_pro;
|
||||
bytes_pro = process_plane(input, width, height, output + 0, size - total_pro);
|
||||
total_pro += bytes_pro;
|
||||
return size == total_pro;
|
||||
}
|
||||
|
||||
int
|
||||
bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
|
||||
uint8 * temp = (uint8*)malloc(input_width * input_height * 2);
|
||||
RD_BOOL rv = bitmap_decompress2(temp, input_width, input_height, input, size);
|
||||
|
||||
// convert to rgba
|
||||
for (int y = 0; y < output_height; y++) {
|
||||
for (int x = 0; x < output_width; x++) {
|
||||
uint16 a = ((uint16*)temp)[y * input_width + x];
|
||||
uint8 r = (a & 0x7c00) >> 10;
|
||||
uint8 g = (a & 0x03e0) >> 5;
|
||||
uint8 b = (a & 0x001f);
|
||||
r = r * 255 / 31;
|
||||
g = g * 255 / 31;
|
||||
b = b * 255 / 31;
|
||||
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
|
||||
}
|
||||
}
|
||||
|
||||
free(temp);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
|
||||
uint8 * temp = (uint8*)malloc(input_width * input_height * 2);
|
||||
RD_BOOL rv = bitmap_decompress2(temp, input_width, input_height, input, size);
|
||||
|
||||
// convert to rgba
|
||||
for (int y = 0; y < output_height; y++) {
|
||||
for (int x = 0; x < output_width; x++) {
|
||||
uint16 a = ((uint16*)temp)[y * input_width + x];
|
||||
uint8 r = (a & 0xf800) >> 11;
|
||||
uint8 g = (a & 0x07e0) >> 5;
|
||||
uint8 b = (a & 0x001f);
|
||||
r = r * 255 / 31;
|
||||
g = g * 255 / 63;
|
||||
b = b * 255 / 31;
|
||||
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
|
||||
}
|
||||
}
|
||||
free(temp);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
bitmap_decompress_24(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
|
||||
uint8 * temp = (uint8*)malloc(input_width * input_height * 3);
|
||||
RD_BOOL rv = bitmap_decompress3(temp, input_width, input_height, input, size);
|
||||
|
||||
// convert to rgba
|
||||
for (int y = 0; y < output_height; y++) {
|
||||
for (int x = 0; x < output_width; x++) {
|
||||
uint8 r = temp[(y * input_width + x) * 3];
|
||||
uint8 g = temp[(y * input_width + x) * 3 + 1];
|
||||
uint8 b = temp[(y * input_width + x) * 3 + 2];
|
||||
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
|
||||
}
|
||||
}
|
||||
free(temp);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
bitmap_decompress_32(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
|
||||
uint8 * temp = (uint8*)malloc(input_width * input_height * 4);
|
||||
RD_BOOL rv = bitmap_decompress4(temp, input_width, input_height, input, size);
|
||||
|
||||
// convert to rgba
|
||||
for (int y = 0; y < output_height; y++) {
|
||||
for (int x = 0; x < output_width; x++) {
|
||||
uint8 r = temp[(y * input_width + x) * 4];
|
||||
uint8 g = temp[(y * input_width + x) * 4 + 1];
|
||||
uint8 b = temp[(y * input_width + x) * 4 + 2];
|
||||
uint8 a = temp[(y * input_width + x) * 4 + 3];
|
||||
((uint32*)output)[y * output_width + x] = 0xff << 24 | r << 16 | g << 8 | b;
|
||||
}
|
||||
}
|
||||
free(temp);
|
||||
|
||||
return rv;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef RLE_H
|
||||
#define RLE_H
|
||||
|
||||
#define RD_BOOL int
|
||||
#define False 0
|
||||
#define True 1
|
||||
|
||||
#define uint8 unsigned char
|
||||
#define uint16 unsigned short
|
||||
#define uint32 unsigned int
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
RD_BOOL bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size);
|
||||
RD_BOOL bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int size);
|
||||
RD_BOOL bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size);
|
||||
RD_BOOL bitmap_decompress4(uint8 * output, int width, int height, uint8 * input, int size);
|
||||
|
||||
//int bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
|
||||
//int bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
|
||||
//int bitmap_decompress_24(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
|
||||
//int bitmap_decompress_32(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RLE_H
|
|
@ -0,0 +1,87 @@
|
|||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#include "thr_play.h"
|
||||
#include "record_format.h"
|
||||
|
||||
static QString REPLAY_PATH = "E:\\work\\tp4a\\teleport\\server\\share\\replay\\rdp\\000000197\\";
|
||||
|
||||
|
||||
ThreadPlay::ThreadPlay()
|
||||
{
|
||||
m_need_stop = false;
|
||||
}
|
||||
|
||||
void ThreadPlay::stop() {
|
||||
m_need_stop = true;
|
||||
}
|
||||
|
||||
void ThreadPlay::run() {
|
||||
qint64 read_len = 0;
|
||||
uint32_t total_pkg = 0;
|
||||
|
||||
QString hdr_filename(REPLAY_PATH);
|
||||
hdr_filename += "tp-rdp.tpr";
|
||||
|
||||
QFile f_hdr(hdr_filename);
|
||||
if(!f_hdr.open(QFile::ReadOnly)) {
|
||||
qDebug() << "Can not open " << hdr_filename << " for read.";
|
||||
return;
|
||||
}
|
||||
else {
|
||||
update_data* dat = new update_data;
|
||||
dat->data_type(TYPE_HEADER_INFO);
|
||||
dat->alloc_data(sizeof(TS_RECORD_HEADER));
|
||||
|
||||
read_len = f_hdr.read((char*)(dat->data_buf()), dat->data_len());
|
||||
if(read_len != sizeof(TS_RECORD_HEADER)) {
|
||||
delete dat;
|
||||
qDebug() << "invaid .tpr file.";
|
||||
return;
|
||||
}
|
||||
|
||||
TS_RECORD_HEADER* hdr = (TS_RECORD_HEADER*)dat->data_buf();
|
||||
total_pkg = hdr->info.packages;
|
||||
|
||||
emit signal_update_data(dat);
|
||||
}
|
||||
|
||||
|
||||
|
||||
QString dat_filename(REPLAY_PATH);
|
||||
dat_filename += "tp-rdp.dat";
|
||||
|
||||
QFile f_dat(dat_filename);
|
||||
if(!f_dat.open(QFile::ReadOnly)) {
|
||||
qDebug() << "Can not open " << dat_filename << " for read.";
|
||||
return;
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < total_pkg; ++i) {
|
||||
if(m_need_stop) {
|
||||
qDebug() << "stop, user cancel.";
|
||||
break;
|
||||
}
|
||||
|
||||
TS_RECORD_PKG pkg;
|
||||
read_len = f_dat.read((char*)(&pkg), sizeof(pkg));
|
||||
if(read_len != sizeof(TS_RECORD_PKG)) {
|
||||
qDebug() << "invaid .dat file.";
|
||||
return;
|
||||
}
|
||||
|
||||
update_data* dat = new update_data;
|
||||
dat->data_type(TYPE_DATA);
|
||||
dat->alloc_data(sizeof(TS_RECORD_PKG) + pkg.size);
|
||||
memcpy(dat->data_buf(), &pkg, sizeof(TS_RECORD_PKG));
|
||||
read_len = f_dat.read((char*)(dat->data_buf()+sizeof(TS_RECORD_PKG)), pkg.size);
|
||||
if(read_len != pkg.size) {
|
||||
delete dat;
|
||||
qDebug() << "invaid .dat file.";
|
||||
return;
|
||||
}
|
||||
|
||||
emit signal_update_data(dat);
|
||||
msleep(5);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef THR_PLAY_H
|
||||
#define THR_PLAY_H
|
||||
|
||||
#include <QThread>
|
||||
#include "update_data.h"
|
||||
|
||||
|
||||
class ThreadPlay : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ThreadPlay();
|
||||
|
||||
virtual void run();
|
||||
void stop();
|
||||
|
||||
|
||||
signals:
|
||||
void signal_update_data(update_data*);
|
||||
|
||||
private:
|
||||
bool m_need_stop;
|
||||
};
|
||||
|
||||
#endif // THR_PLAY_H
|
|
@ -0,0 +1,26 @@
|
|||
TEMPLATE = app
|
||||
TARGET = tp-player
|
||||
|
||||
QT += core gui widgets
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
thr_play.h \
|
||||
update_data.h \
|
||||
record_format.h \
|
||||
rle.h
|
||||
|
||||
SOURCES += main.cpp \
|
||||
mainwindow.cpp \
|
||||
thr_play.cpp \
|
||||
update_data.cpp \
|
||||
rle.c
|
||||
|
||||
RESOURCES += \
|
||||
tp-player.qrc
|
||||
|
||||
RC_FILE += \
|
||||
tp-player.rc
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
|
@ -0,0 +1,7 @@
|
|||
<RCC>
|
||||
<qresource prefix="/tp-player">
|
||||
<file>res/logo.png</file>
|
||||
<file>res/bg.png</file>
|
||||
<file>res/cursor.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,2 @@
|
|||
IDI_ICON1 ICON DISCARDABLE "res\\tp-player.ico"
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#include "update_data.h"
|
||||
|
||||
update_data::update_data(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_data_type = 0xff;
|
||||
m_data_buf = nullptr;
|
||||
m_data_len = 0;
|
||||
}
|
||||
|
||||
update_data::~update_data() {
|
||||
if(m_data_buf)
|
||||
delete m_data_buf;
|
||||
}
|
||||
|
||||
void update_data::alloc_data(uint32_t len) {
|
||||
if(m_data_buf)
|
||||
delete m_data_buf;
|
||||
|
||||
m_data_buf = new uint8_t[len];
|
||||
memset(m_data_buf, 0, len);
|
||||
m_data_len = len;
|
||||
}
|
||||
|
||||
void update_data::attach_data(const uint8_t* dat, uint32_t len) {
|
||||
if(m_data_buf)
|
||||
delete m_data_buf;
|
||||
m_data_buf = new uint8_t[len];
|
||||
memcpy(m_data_buf, dat, len);
|
||||
m_data_len = len;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef UPDATE_DATA_H
|
||||
#define UPDATE_DATA_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class update_data : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit update_data(QObject *parent = nullptr);
|
||||
virtual ~update_data();
|
||||
|
||||
void alloc_data(uint32_t len);
|
||||
void attach_data(const uint8_t* dat, uint32_t len);
|
||||
|
||||
void data_type(int dt) {m_data_type = dt;}
|
||||
int data_type() const {return m_data_type;}
|
||||
|
||||
uint8_t* data_buf() {return m_data_buf;}
|
||||
uint32_t data_len() const {return m_data_len;}
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
private:
|
||||
int m_data_type;
|
||||
uint8_t* m_data_buf;
|
||||
uint32_t m_data_len;
|
||||
};
|
||||
|
||||
#endif // UPDATE_DATA_H
|
Binary file not shown.
Loading…
Reference in New Issue