mirror of
https://github.com/thug1src/thug.git
synced 2024-12-02 12:56:45 +00:00
348 lines
8.3 KiB
C++
348 lines
8.3 KiB
C++
|
/*---------------------------------------------------------------------------*
|
||
|
Project: onetri
|
||
|
File: screenshot.h
|
||
|
|
||
|
Copyright 1998, 1999, 2000 Nintendo. All rights reserved.
|
||
|
|
||
|
These coded instructions, statements, and computer programs contain
|
||
|
proprietary information of Nintendo of America Inc. and/or Nintendo
|
||
|
Company Ltd., and are protected by Federal copyright law. They may
|
||
|
not be disclosed to third parties or copied or duplicated in any form,
|
||
|
in whole or in part, without the prior written consent of Nintendo.
|
||
|
|
||
|
Created 05-25-01 by Steve Rabin
|
||
|
|
||
|
*---------------------------------------------------------------------------*/
|
||
|
|
||
|
#include <sys/ngc/p_screenshot.h>
|
||
|
#include <dolphin/hio.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/ngc/p_gx.h>
|
||
|
|
||
|
|
||
|
#define WIDTH_SCREENSHOT 640
|
||
|
#define HEIGHT_SCREENSHOT 480
|
||
|
#define SIZE_SCREENSHOT_RGB WIDTH_SCREENSHOT*HEIGHT_SCREENSHOT*3
|
||
|
#define SIZE_SCREENSHOT_YUV WIDTH_SCREENSHOT*HEIGHT_SCREENSHOT*2
|
||
|
|
||
|
#define XFB_BASE 1
|
||
|
#define XFB_RANGE 6
|
||
|
#define EFB_BASE 11
|
||
|
#define EFB_RANGE 9
|
||
|
#define PING_BASE 20
|
||
|
#define PING_RANGE 9
|
||
|
#define MINIMIZE_BUFFER_TRUE 35
|
||
|
#define MINIMIZE_BUFFER_FALSE 36
|
||
|
|
||
|
|
||
|
typedef enum {
|
||
|
GRAB_NOT_TRANSFERRING,
|
||
|
GRAB_TRANSFERRING
|
||
|
} GRAB_STATUS;
|
||
|
|
||
|
|
||
|
//Globals
|
||
|
BOOL g_connected = FALSE;
|
||
|
BOOL g_ping_received = FALSE;
|
||
|
u32 g_received_signal = 0;
|
||
|
BOOL g_mail_waiting = FALSE;
|
||
|
BOOL g_minimize_buffer = FALSE;
|
||
|
s32 g_usb_channel = -1;
|
||
|
u8* g_data;
|
||
|
|
||
|
|
||
|
static BOOL HostIOEnumCallback( s32 chan )
|
||
|
{
|
||
|
g_usb_channel = chan;
|
||
|
return( FALSE );
|
||
|
}
|
||
|
|
||
|
static void HostIOCallback( void )
|
||
|
{
|
||
|
//Mail is waiting for us - don't look at it yet
|
||
|
g_mail_waiting = TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void CopyoutEFB( u8* image_buffer, u32 width, u32 height )
|
||
|
{
|
||
|
u16 i, j;
|
||
|
u32 color;
|
||
|
|
||
|
for( j=0; j<height; j++ ) {
|
||
|
for( i=0; i<width; i++ ) {
|
||
|
GX::PeekARGB( i, j, &color );
|
||
|
image_buffer[2] = (u8)(color & 0x000000FF);
|
||
|
image_buffer[1] = (u8)((color & 0x0000FF00) >> 8);
|
||
|
image_buffer[0] = (u8)((color & 0x00FF0000) >> 16);
|
||
|
image_buffer += 3;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void CopyoutPortionEFB( u8* image_buffer, u32 width, u32 startByte, u32 totalBytes )
|
||
|
{
|
||
|
u32 color;
|
||
|
|
||
|
u16 x = (u16)((u32)(startByte/3) % width);
|
||
|
u16 y = (u16)((u32)(startByte/3) / width);
|
||
|
u32 component = startByte%3;
|
||
|
|
||
|
u32 count = 0;
|
||
|
while( count < totalBytes )
|
||
|
{
|
||
|
GX::PeekARGB( x, y, &color );
|
||
|
|
||
|
if( component == 0 ) {
|
||
|
count++;
|
||
|
image_buffer[0] = (u8)((color & 0x00FF0000) >> 16);
|
||
|
}
|
||
|
if( component <= 1 && count < totalBytes ) {
|
||
|
count++;
|
||
|
image_buffer[1 - component] = (u8)((color & 0x0000FF00) >> 8);
|
||
|
}
|
||
|
if( component <= 2 && count < totalBytes ) {
|
||
|
count++;
|
||
|
image_buffer[2 - component] = (u8)(color & 0x000000FF);
|
||
|
}
|
||
|
image_buffer += 3 - component;
|
||
|
component = 0;
|
||
|
|
||
|
x++;
|
||
|
if( x >= width ) {
|
||
|
x = 0;
|
||
|
y++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void TakeScreenshotEFB( SCREENSHOTAllocator allocator )
|
||
|
{
|
||
|
if( g_data == 0 ) {
|
||
|
g_data = (u8*)allocator( SIZE_SCREENSHOT_RGB );
|
||
|
}
|
||
|
CopyoutEFB( g_data, WIDTH_SCREENSHOT, HEIGHT_SCREENSHOT );
|
||
|
DCFlushRange( &g_data[0], SIZE_SCREENSHOT_RGB );
|
||
|
OSReport( "SCREENSHOT: Saved off screenshot to local memory.\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void TakeScreenshotXFB( void* bufferXFB, SCREENSHOTAllocator allocator )
|
||
|
{
|
||
|
void* buffer = bufferXFB;
|
||
|
if( g_data == 0 ) {
|
||
|
g_data = (u8*)allocator( SIZE_SCREENSHOT_YUV );
|
||
|
}
|
||
|
memcpy( g_data, buffer, SIZE_SCREENSHOT_YUV );
|
||
|
DCFlushRange( &g_data[0], SIZE_SCREENSHOT_YUV );
|
||
|
OSReport( "SCREENSHOT: Saved off screenshot to local memory.\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void WriteScreenshotPortionEFBtoUSB( u32 chunk, SCREENSHOTAllocator allocator )
|
||
|
{
|
||
|
u32 i;
|
||
|
u32 size1K = 1024;
|
||
|
u32 size100K = size1K*100;
|
||
|
if( g_data == 0 ) {
|
||
|
g_data = (u8*)allocator( size1K );
|
||
|
}
|
||
|
for( i=0; i<100; i++ )
|
||
|
{
|
||
|
CopyoutPortionEFB( g_data, WIDTH_SCREENSHOT, (chunk*size100K)+(i*size1K), size1K );
|
||
|
DCFlushRange( &g_data[0], size1K );
|
||
|
while( !HIOWrite( 0x00000500 + (i*size1K), g_data, (s32)size1K ) ) {
|
||
|
//Spin until communication is successful
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static GRAB_STATUS GrabChunk( u32 chunk,
|
||
|
void* bufferXFB,
|
||
|
SCREENSHOTAllocator allocator,
|
||
|
SCREENSHOTDeallocator deallocator )
|
||
|
{
|
||
|
if( chunk >= XFB_BASE && chunk < XFB_BASE + XFB_RANGE )
|
||
|
{
|
||
|
void* buffer = 0;
|
||
|
|
||
|
if( chunk == XFB_BASE )
|
||
|
{ //save off XFB screenshot
|
||
|
if( !g_minimize_buffer ) {
|
||
|
TakeScreenshotXFB( bufferXFB, allocator );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//transfer chunk
|
||
|
if( g_minimize_buffer ) {
|
||
|
u8* fb = (u8*)bufferXFB;
|
||
|
buffer = (void*)( &fb[100*1024*(chunk - XFB_BASE)] );
|
||
|
}
|
||
|
else {
|
||
|
buffer = (void*)( &g_data[100*1024*(chunk - XFB_BASE)] );
|
||
|
}
|
||
|
|
||
|
while( !HIOWrite( 0x00000500, buffer, 100*1024 ) ) {
|
||
|
//Spin until communication is successful
|
||
|
}
|
||
|
OSReport( "SCREENSHOT: Wrote chunk #%d.\n", chunk );
|
||
|
|
||
|
//communicate that the chunk is ready to be read
|
||
|
while( !HIOWriteMailbox( chunk ) ) {
|
||
|
//Spin until communication is successful
|
||
|
}
|
||
|
OSReport( "SCREENSHOT: Notify of write chunk #%d put in mailbox.\n", g_received_signal );
|
||
|
}
|
||
|
else if( chunk >= EFB_BASE && chunk < EFB_BASE + EFB_RANGE )
|
||
|
{
|
||
|
void* buffer;
|
||
|
|
||
|
if( chunk == EFB_BASE )
|
||
|
{ //save off EFB screenshot
|
||
|
if( !g_minimize_buffer ) {
|
||
|
TakeScreenshotEFB( allocator );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( g_minimize_buffer )
|
||
|
{
|
||
|
WriteScreenshotPortionEFBtoUSB( chunk - EFB_BASE, allocator );
|
||
|
}
|
||
|
else
|
||
|
{ //transfer chunk
|
||
|
buffer = (void*)( &g_data[100*1024*(chunk - EFB_BASE)] );
|
||
|
while( !HIOWrite( 0x00000500, buffer, 100*1024 ) ) {
|
||
|
//Spin until communication is successful
|
||
|
}
|
||
|
}
|
||
|
OSReport( "SCREENSHOT: Wrote chunk #%d.\n", chunk );
|
||
|
|
||
|
//communicate that the chunk is ready to be read
|
||
|
while( !HIOWriteMailbox( chunk ) ) {
|
||
|
//Spin until communication is successful
|
||
|
}
|
||
|
OSReport( "SCREENSHOT: Notify of write chunk #%d put in mailbox.\n", g_received_signal );
|
||
|
|
||
|
}
|
||
|
|
||
|
if( chunk == XFB_BASE + XFB_RANGE - 1 ||
|
||
|
chunk == EFB_BASE + EFB_RANGE - 1 )
|
||
|
{ //free screenshot buffer space
|
||
|
if( g_data ) {
|
||
|
deallocator( g_data );
|
||
|
g_data = 0;
|
||
|
}
|
||
|
return( GRAB_NOT_TRANSFERRING );
|
||
|
}
|
||
|
else {
|
||
|
return( GRAB_TRANSFERRING );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void CheckMail( void* bufferXFB,
|
||
|
SCREENSHOTAllocator allocator,
|
||
|
SCREENSHOTDeallocator deallocator )
|
||
|
{
|
||
|
u32 temp = 0;
|
||
|
u32 escapeCount = 0;
|
||
|
GRAB_STATUS status = GRAB_TRANSFERRING;
|
||
|
while( status == GRAB_TRANSFERRING )
|
||
|
{
|
||
|
escapeCount++;
|
||
|
if( g_mail_waiting )
|
||
|
{
|
||
|
if( HIOReadMailbox( &temp ) ) {
|
||
|
g_mail_waiting = FALSE;
|
||
|
status = GRAB_NOT_TRANSFERRING;
|
||
|
if( temp >= PING_BASE && temp < PING_BASE + PING_RANGE )
|
||
|
{ //ping message received
|
||
|
while( !HIOWriteMailbox( temp ) ) {
|
||
|
//Spin until communication is successful
|
||
|
}
|
||
|
OSReport( "SCREENSHOT: Sent ping back.\n" );
|
||
|
}
|
||
|
else if( (temp >= XFB_BASE && temp < XFB_BASE + XFB_RANGE) ||
|
||
|
(temp >= EFB_BASE && temp < EFB_BASE + EFB_RANGE) )
|
||
|
{ //request for screen capture
|
||
|
escapeCount = 0;
|
||
|
g_received_signal = temp;
|
||
|
OSReport( "SCREENSHOT: Grab screenshot request.\n" );
|
||
|
status = GrabChunk( g_received_signal, bufferXFB, allocator, deallocator );
|
||
|
}
|
||
|
else if( temp == MINIMIZE_BUFFER_TRUE )
|
||
|
{ //request to use minimal memory (100K), however game pauses
|
||
|
g_minimize_buffer = TRUE;
|
||
|
}
|
||
|
else if( temp == MINIMIZE_BUFFER_FALSE )
|
||
|
{ //request to not use minimal memory and keep game running
|
||
|
g_minimize_buffer = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if( !g_minimize_buffer )
|
||
|
{ //Escape loop since were not pausing the game to transfer
|
||
|
return;
|
||
|
}
|
||
|
if( g_minimize_buffer && escapeCount > 150000000 )
|
||
|
{ //The PC screenshot app has died - escape
|
||
|
if( g_data ) {
|
||
|
deallocator( g_data );
|
||
|
g_data = 0;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static BOOL ConnectToUSB( void )
|
||
|
{
|
||
|
if( !g_connected )
|
||
|
{
|
||
|
if( HIOEnumDevices( HostIOEnumCallback ) ) {
|
||
|
if( g_usb_channel >= 0 ) {
|
||
|
if( HIOInit( g_usb_channel, HostIOCallback ) ) {
|
||
|
u32 temp = 0;
|
||
|
HIOReadMailbox( &temp );
|
||
|
g_connected = TRUE;
|
||
|
OSReport( "SCREENSHOT: USB connected\n" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return( g_connected );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void SCREENSHOTService( void* bufferXFB,
|
||
|
SCREENSHOTAllocator allocator,
|
||
|
SCREENSHOTDeallocator deallocator )
|
||
|
{
|
||
|
if( !g_connected ) {
|
||
|
ConnectToUSB();
|
||
|
}
|
||
|
else if( g_mail_waiting ) {
|
||
|
CheckMail( bufferXFB, allocator, deallocator );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|