.temp. new rdp-player.

pull/175/head^2
Apex Liu 2019-09-03 05:40:52 +08:00
parent f5b00ca02a
commit 0b020810b2
18 changed files with 1609 additions and 0 deletions

15
client/tp-player/main.cpp Normal file
View File

@ -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();
}

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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

BIN
client/tp-player/res/bg.png Normal file

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

974
client/tp-player/rle.c Normal file
View File

@ -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;
}

31
client/tp-player/rle.h Normal file
View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -0,0 +1,2 @@
IDI_ICON1 ICON DISCARDABLE "res\\tp-player.ico"

View File

@ -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;
}

View File

@ -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

BIN
resource/icon-tp-player.psd Normal file

Binary file not shown.