301 lines
No EOL
7.5 KiB
C++
301 lines
No EOL
7.5 KiB
C++
// A lot of this code is made because of the work of https://github.com/capull0/SkyDumper and https://github.com/silicontrip/SkyReader
|
|
|
|
#include "SkyPortalSubsystem.h"
|
|
#include "Engine/Engine.h"
|
|
|
|
#include "HAL/RunnableThread.h"
|
|
#include "SkyPortalFigure.h"
|
|
|
|
|
|
void USkyPortalSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
|
|
Super::Initialize(Collection);
|
|
FigureArray.SetNum(16);
|
|
// Create UFigureData objects dynamically
|
|
for (uint8 i = 0; i < FigureArray.Num(); i++)
|
|
{
|
|
FigureArray[i] = NewObject<UFigureData>(this);
|
|
}
|
|
UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Initialized"));
|
|
|
|
}
|
|
|
|
void USkyPortalSubsystem::Deinitialize()
|
|
{
|
|
// Stop the thread when the subsystem is deinitialized
|
|
if (StatusChecker)
|
|
{
|
|
StatusChecker->Stop();
|
|
StatusCheckerThread->WaitForCompletion();
|
|
|
|
delete StatusCheckerThread;
|
|
delete StatusChecker;
|
|
StatusChecker = nullptr;
|
|
StatusCheckerThread = nullptr;
|
|
}
|
|
|
|
// Disconnect portal
|
|
if (PortalHandle) {
|
|
PortalHandle->Close();
|
|
}
|
|
hid_exit();
|
|
|
|
|
|
UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Deinitialized"));
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
|
|
void USkyPortalSubsystem::PortalActivate(const bool bShouldActivate)
|
|
{
|
|
FWriteBlock command;
|
|
|
|
memset(command.data, 0, write_buf_size);
|
|
command.data[1] = 'A';
|
|
command.data[2] = bShouldActivate;
|
|
uint8* output;
|
|
do {
|
|
PortalHandle->Write(&command);
|
|
output = PortalHandle->Read();
|
|
} while (output[0] != 'A');
|
|
|
|
}
|
|
|
|
|
|
|
|
void USkyPortalSubsystem::PortalReady()
|
|
{
|
|
FWriteBlock command;
|
|
|
|
memset(command.data, 0, write_buf_size); //maybe not needed here
|
|
command.data[1] = 'R';
|
|
unsigned char* output;
|
|
do {
|
|
PortalHandle->Write(&command);
|
|
output = PortalHandle->Read();
|
|
} while (output[0] != 'R');
|
|
|
|
unsigned char _PortalId[2] = { output[1],output[2] };
|
|
uint16_t BE_PortalId = (_PortalId[0] << 8) | _PortalId[1]; // Big-endian format
|
|
uint32 BEU_PortalId = (_PortalId[0] << 8) | _PortalId[1]; // Big-endian format unsigned
|
|
uint16_t LE_PortalId = (_PortalId[1] << 8) | _PortalId[0]; //little endian
|
|
//uint32 LEU_PortalId = (_PortalId[1] << 8) | _PortalId[0]; //little endian unsigned
|
|
PortalId = BEU_PortalId; //Need a conversion as uint16 is not supported in BP
|
|
|
|
}
|
|
|
|
FPortalStatusData USkyPortalSubsystem::PortalStatus() {
|
|
FWriteBlock command;
|
|
|
|
memset(command.data, 0, write_buf_size); //maybe not needed here
|
|
command.data[1] = 'S';
|
|
uint8* output;
|
|
|
|
do {
|
|
PortalHandle->Write(&command);
|
|
output = PortalHandle->Read();
|
|
} while (output[0] != 'S');
|
|
|
|
|
|
|
|
return ParsePortalStatus(output);
|
|
}
|
|
|
|
SKYPORTAL_API bool USkyPortalSubsystem::RestartRunner()
|
|
{
|
|
if (StatusChecker)
|
|
{
|
|
StatusChecker->Stop();
|
|
StatusCheckerThread->WaitForCompletion();
|
|
|
|
delete StatusCheckerThread;
|
|
delete StatusChecker;
|
|
StatusChecker = nullptr;
|
|
StatusCheckerThread = nullptr;
|
|
}
|
|
|
|
StatusChecker = new FPortalStatusChecker(this, RunnerInterval); // Check every 10 milliseconds
|
|
StatusCheckerThread = FRunnableThread::Create(StatusChecker, TEXT("PortalStatusCheckerThread"), 0, TPri_AboveNormal);
|
|
|
|
UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Initialized"));
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma region Color functions
|
|
|
|
void USkyPortalSubsystem::ChangePortalColor(const FLinearColor& Color)
|
|
{
|
|
|
|
unsigned char r = FMath::Clamp(Color.R * 100, 0.0f, 255.0f);
|
|
unsigned char g = FMath::Clamp(Color.G * 100, 0.0f, 255.0f);
|
|
unsigned char b = FMath::Clamp(Color.B * 100, 0.0f, 255.0f);
|
|
|
|
FWriteBlock command;
|
|
|
|
memset(command.data, 0, write_buf_size);
|
|
|
|
command.data[1] = 'C';
|
|
command.data[2] = r; // R
|
|
command.data[3] = g; // G
|
|
command.data[4] = b; // B
|
|
|
|
// no response for this one.
|
|
PortalHandle->Write(&command);
|
|
}
|
|
|
|
void USkyPortalSubsystem::ChangePortalColorSide(const FLinearColor& Color, const EPortalSide PortalSide, const float BlendTime)
|
|
{
|
|
|
|
unsigned char r = FMath::Clamp(Color.R * 100, 0, 255);
|
|
unsigned char g = FMath::Clamp(Color.G * 100, 0, 255);
|
|
unsigned char b = FMath::Clamp(Color.B * 100, 0, 255);
|
|
EPortalSide _portalside;
|
|
FWriteBlock command;
|
|
|
|
memset(command.data, 0, write_buf_size);
|
|
|
|
if (PortalSide == EPortalSide::BOTH) {
|
|
_portalside = EPortalSide::LEFT;
|
|
}
|
|
else {
|
|
_portalside = PortalSide;
|
|
}
|
|
|
|
switch (_portalside) {
|
|
case EPortalSide::LEFT:
|
|
command.data[1] = 'J';
|
|
command.data[2] = 0x00;
|
|
case EPortalSide::RIGHT:
|
|
command.data[1] = 'J';
|
|
command.data[2] = 0x02;
|
|
case EPortalSide::TRAP:
|
|
command.data[1] = 'L';
|
|
command.data[2] = 0x01;
|
|
command.data[3] = FMath::Max3(r, g, b); // calculate brightness
|
|
PortalHandle->Write(&command); //since it's a color command for trap, only 3 bytes are needed, no response.
|
|
return;
|
|
}
|
|
|
|
command.data[3] = r; // R
|
|
command.data[4] = g; // G
|
|
command.data[5] = b; // B
|
|
|
|
//Convert the time in millisecond into two bytes
|
|
uint16_t _time = BlendTime;
|
|
uint8_t _time_low = _time & 0xFF; // Get the low byte by masking the least significant 8 bits
|
|
uint8_t _time_high = (_time >> 8) & 0xFF; // Get the high byte extracted by shifting the bits 8 positions to the right and masking the result
|
|
command.data[6] = _time_low;
|
|
command.data[7] = _time_high;
|
|
|
|
uint8* output;
|
|
do {
|
|
PortalHandle->Write(&command);
|
|
output = PortalHandle->Read();
|
|
} while (output[0] != 'J');
|
|
|
|
if (PortalSide == EPortalSide::BOTH) {
|
|
ChangePortalColorSide(Color, EPortalSide::RIGHT, BlendTime); // send a second command for the right side
|
|
}
|
|
}
|
|
#pragma endregion
|
|
|
|
|
|
|
|
|
|
void USkyPortalSubsystem::Sleep(int sleepMs) {
|
|
FPlatformProcess::Sleep(sleepMs * 0.0001);
|
|
}
|
|
|
|
|
|
bool USkyPortalSubsystem::PortalConnect()
|
|
{
|
|
if (StatusChecker)
|
|
{
|
|
StatusChecker->Stop();
|
|
StatusCheckerThread->WaitForCompletion();
|
|
|
|
delete StatusCheckerThread;
|
|
delete StatusChecker;
|
|
StatusChecker = nullptr;
|
|
StatusCheckerThread = nullptr;
|
|
}
|
|
|
|
PortalHandle = NewObject<USkyPortalIO>();
|
|
if (IsValid(PortalHandle) && PortalHandle->bPortalReady) {
|
|
UE_LOG(LogSkyportalIO, Log, TEXT("Portal connected: "));
|
|
PortalReady();
|
|
PortalActivate(1);
|
|
|
|
Sleep(500);
|
|
ChangePortalColor(FLinearColor(0.5, 0.5, 0.5));
|
|
|
|
// Start the status checker thread
|
|
StatusChecker = new FPortalStatusChecker(this, RunnerInterval);
|
|
StatusCheckerThread = FRunnableThread::Create(StatusChecker, TEXT("PortalStatusCheckerThread"), 0, TPri_AboveNormal);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool USkyPortalSubsystem::IsPortalReady()
|
|
{
|
|
if (PortalHandle) {
|
|
return PortalHandle->bPortalReady;
|
|
}
|
|
//maybe should send a status request ?
|
|
return false;
|
|
}
|
|
|
|
void USkyPortalSubsystem::PortalMusic(const USoundWave* Sound)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void USkyPortalSubsystem::PortalAnalyzeAsync(const uint8 Index)
|
|
{
|
|
bShouldPauseRunner = true;
|
|
UE_LOG(LogSkyportalIO, Log, TEXT("Starting async figure analysis..."));
|
|
|
|
// Run the PortalAnalyze function asynchronously on a background thread
|
|
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this, Index]()
|
|
{
|
|
// Perform the blocking operation on the background thread
|
|
PortalAnalyze(Index);
|
|
|
|
// Once done, switch back to the main game thread
|
|
AsyncTask(ENamedThreads::GameThread, [this, Index]()
|
|
{
|
|
// This will be executed on the game thread after the async task is complete
|
|
bShouldPauseRunner = false;
|
|
UE_LOG(LogSkyportalIO, Log, TEXT("Figure analysis complete for index: %d"), Index);
|
|
});
|
|
});
|
|
}
|
|
|
|
void USkyPortalSubsystem::PortalAnalyze(const uint8 index)
|
|
{
|
|
|
|
UE_LOG(LogSkyportalIO, Log, TEXT("Reading figure..."));
|
|
if (FigureArray.IsValidIndex(index)) {
|
|
FigureArray[index] = PortalHandle->ReadFigureBlocks(index);
|
|
UE_LOG(LogSkyportalIO, Log, TEXT("Figure ID : %d"), FigureArray[index]->GetFigureID());
|
|
return;
|
|
}
|
|
UE_LOG(LogSkyportalIO, Error, TEXT("Error in FigureArray"));
|
|
|
|
|
|
|
|
}
|
|
|
|
void USkyPortalSubsystem::GetFigureArray() {
|
|
FigureArray;
|
|
for (int i = 0; 16; i++) {
|
|
FigureArray[i] = PortalHandle->ReadFigureBlocks(i);
|
|
}
|
|
return;
|
|
} |