Merge branch 'cryptography'
This commit is contained in:
commit
0797c9488a
10 changed files with 297 additions and 149 deletions
|
@ -14,6 +14,10 @@ void FSkyPortalModule::ShutdownModule()
|
|||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
|
||||
if (GEngine->GetEngineSubsystem<USkyPortalSubsystem>()) {
|
||||
GEngine->GetEngineSubsystem<USkyPortalSubsystem>()->Deinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
|
||||
#include "SkyPortalFigure.h"
|
||||
|
||||
UFigureData::UFigureData()
|
||||
{
|
||||
ClearData();
|
||||
}
|
||||
|
||||
uint32 FigureData::GetFigureID()
|
||||
int32 UFigureData::GetFigureID()
|
||||
{
|
||||
if (ID == 0) {
|
||||
int16_t OutFigureID = 0;
|
||||
|
||||
// The figure ID is stored in Block 1 of Sector 0 (i.e., data[1]), bytes 0 and 1 (16-bit integer, little-endian)
|
||||
OutFigureID = data[1][0] | // Least significant byte
|
||||
OutFigureID = decryptedData[1][0] | // Least significant byte
|
||||
(data[1][1] << 8); // Most significant byte, shifted left by 8 bits
|
||||
|
||||
ID = OutFigureID;// Return the 16-bit figure ID
|
||||
|
@ -25,64 +29,6 @@ uint32 FigureData::GetFigureID()
|
|||
|
||||
};
|
||||
|
||||
/*
|
||||
void FigureData::ReadData(uint8 index)
|
||||
{
|
||||
ClearData();
|
||||
for (uint8 i = 0; i < 64; i++)
|
||||
{
|
||||
GEngine->GetEngineSubsystem<USkyPortalSubsystem>();
|
||||
TArray<uint8> output = SkySubsystem->QueryBlock(index, i);
|
||||
|
||||
uint8[] blockData = new uint8[0x10];
|
||||
Array.Copy(output, 3, blockData, 0, 16);
|
||||
|
||||
// block 1 is sometimes a duplicate of block 0
|
||||
if (i == 1)
|
||||
{
|
||||
if (blockData.SequenceEqual(data[0]))
|
||||
{
|
||||
i -= 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Array.Copy(blockData, 0, data[i], 0, 16);
|
||||
|
||||
if (((i + 1) % 4 == 0) || i < 8)
|
||||
{
|
||||
Array.Copy(data[i], 0, decryptedData[i], 0, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8[] hashIn = new byte[0x56];
|
||||
data[0].CopyTo(hashIn, 0);
|
||||
data[1].CopyTo(hashIn, 0x10);
|
||||
hashIn[0x20] = i;
|
||||
HASH_CONST.CopyTo(hashIn, 0x21);
|
||||
|
||||
uint8[] key = MD5.Create().ComputeHash(hashIn);
|
||||
|
||||
uint8[] decrypted = new byte[0x10];
|
||||
|
||||
using (Aes aesAlg = Aes.Create())
|
||||
{
|
||||
aesAlg.Key = key;
|
||||
aesAlg.Mode = CipherMode.ECB;
|
||||
aesAlg.Padding = PaddingMode.Zeros;
|
||||
|
||||
ICryptoTransform decryptor = aesAlg.CreateDecryptor();
|
||||
|
||||
decrypted = decryptor.TransformFinalBlock(data[i], 0, data[i].Length);
|
||||
|
||||
}
|
||||
|
||||
Array.Copy(decrypted, 0, decryptedData[i], 0, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void FigureData::ClearData()
|
||||
void UFigureData::ClearData()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#include "SkyPortalIO.h"
|
||||
#include "Misc/AES.h"
|
||||
|
||||
#include "HAL/UnrealMemory.h"
|
||||
#include "openssl/aes.h"
|
||||
|
||||
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogHIDApi);
|
||||
DEFINE_LOG_CATEGORY(LogSkyportalIO);
|
||||
|
@ -104,11 +110,11 @@ struct hid_device_ {
|
|||
};
|
||||
|
||||
|
||||
void USkyPortalIO::Write(FWriteBlock* pb)
|
||||
bool USkyPortalIO::Write(FWriteBlock* pb)
|
||||
{
|
||||
if (!ensure(PortalDevice)) {
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("No Portal found"));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
BOOL res;
|
||||
|
||||
|
@ -124,11 +130,12 @@ void USkyPortalIO::Write(FWriteBlock* pb)
|
|||
&bytes_returned, &ol);
|
||||
ensureMsgf(res, TEXT("Unable to write to Portal"));
|
||||
pb->BytesTransferred = bytes_returned;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
void USkyPortalIO::Write(FWriteBlock* pb) {
|
||||
bool USkyPortalIO::Write(FWriteBlock* pb) {
|
||||
|
||||
if (!ensure(PortalDevice)) {
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("No Portal found"));
|
||||
|
@ -140,6 +147,7 @@ void USkyPortalIO::Write(FWriteBlock* pb) {
|
|||
res = hid_write(PortalDevice, pb->data, write_buf_size);
|
||||
ensureMsgf(res != -1, TEXT("Unable to write to Portal, %s"), hid_error(PortalDevice));
|
||||
pb->BytesTransferred = res;
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -156,14 +164,52 @@ void USkyPortalIO::WriteRaw(const TArray<uint8>* block)
|
|||
|
||||
uint8* USkyPortalIO::Read()
|
||||
{
|
||||
uint8* output = new byte[0x20];
|
||||
hid_read(PortalDevice, output, 0x20);
|
||||
uint8* output = new uint8[0x20];
|
||||
int result = hid_read(PortalDevice, output, 0x20);
|
||||
|
||||
if (result == -1) {
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("Unable to read data from Portal. Error:\n%s"), hid_error(PortalDevice));
|
||||
delete[] output; // Prevent memory leak
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (result == 0) { // No data was read
|
||||
UE_LOG(LogSkyportalIO, Warning, TEXT("No data read from portal"));
|
||||
delete[] output;
|
||||
return nullptr;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
TArray<uint8> USkyPortalIO::QueryBlock(uint8 characterIndex, uint8 block)
|
||||
uint8* USkyPortalIO::QueryBlock(uint8 FigureIndex, uint8 BlockIndex)
|
||||
{
|
||||
return TArray<uint8>();
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Querying block %d - Start"), BlockIndex);
|
||||
FWriteBlock command;
|
||||
memset(command.data, 0, write_buf_size); //maybe not needed here
|
||||
command.data[1] = 'Q';
|
||||
command.data[2] = FigureIndex; // Figure index (0x00-0x0F)
|
||||
command.data[3] = BlockIndex;
|
||||
unsigned char* output = nullptr;
|
||||
int attempt=0;
|
||||
bool write;
|
||||
do {
|
||||
attempt++;
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Trying to write... attempt:%d"), attempt);
|
||||
write = Write(&command);
|
||||
if (write) {
|
||||
output = Read();
|
||||
if (output != nullptr && output != 0) {
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Read success: output[0]=%d, output[1]=%d, output[2]=%d"), output[0], output[1], output[2]);
|
||||
}else{
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("Read failed, output is null"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} while (write == false && (output[0] != 'Q' || (output[1] % 0x10 != FigureIndex && output[1] != 0x01) || output[2] != BlockIndex) && attempt < 10);
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Querying block %d - success"), BlockIndex);
|
||||
UE_LOG(LogSkyportalIO, VeryVerbose, TEXT("Data block %d = \n %s"), BlockIndex,*OutputToString(output));
|
||||
return output;
|
||||
}
|
||||
|
||||
void USkyPortalIO::Close() {
|
||||
|
@ -212,6 +258,25 @@ EPortalCommand GetPortalCommandFromChar(unsigned char Char)
|
|||
|
||||
}
|
||||
|
||||
FString DataToString(const uint8* data)
|
||||
{
|
||||
FString OutString;
|
||||
for (int32 i = 0; i < 16; i++)
|
||||
{
|
||||
OutString += FString::Printf(TEXT("%02x"), data[i]);
|
||||
}
|
||||
return OutString;
|
||||
}
|
||||
|
||||
FString OutputToString(const unsigned char* output) {
|
||||
FString OutString;
|
||||
for (int32 i = 0; i < 0x20; i++)
|
||||
{
|
||||
OutString += FString::Printf(TEXT("%02x"), output[i]);
|
||||
}
|
||||
return OutString;
|
||||
}
|
||||
|
||||
FPortalStatusData ParsePortalStatus(const uint8* StatusResponse)
|
||||
{
|
||||
FPortalStatusData result;
|
||||
|
@ -265,56 +330,94 @@ FPortalStatusData ParsePortalStatus(const uint8* StatusResponse)
|
|||
}
|
||||
|
||||
|
||||
void DecryptAES128(uint8* OutData, const uint8* InData, const uint8* Key)
|
||||
{
|
||||
UE_LOG(LogSkyportalIO, VeryVerbose, TEXT("DECRYPT -- Starting decryting data %s with %s key"), *DataToString(InData), *DataToString(Key));
|
||||
AES_KEY AesKey;
|
||||
// Set the decryption key (16 bytes for AES-128)
|
||||
AES_set_decrypt_key(Key, 128, &AesKey);
|
||||
|
||||
FigureData USkyPortalIO::ReadFigureBlocks(uint8 FigureIndex)
|
||||
// Perform AES-128 decryption (ECB mode)
|
||||
AES_ecb_encrypt(InData, OutData, &AesKey, AES_DECRYPT);
|
||||
UE_LOG(LogSkyportalIO, VeryVerbose, TEXT("DECRYPT -- data %s with %s key decryted"), *DataToString(InData), *DataToString(Key));
|
||||
}
|
||||
|
||||
UFigureData* USkyPortalIO::ReadFigureBlocks(uint8 FigureIndex)
|
||||
{
|
||||
|
||||
FigureData FigureData;
|
||||
FigureData.dataError = false; // Initialize error flag
|
||||
UFigureData* TempFigureData = NewObject<UFigureData>();
|
||||
TempFigureData->dataError = false; // Initialize error flag
|
||||
|
||||
|
||||
FWriteBlock command;
|
||||
|
||||
// Loop over all 64 blocks
|
||||
for (uint8 BlockIndex = 0; BlockIndex < FIGURE_TOTAL_BLOCKS; ++BlockIndex)
|
||||
{
|
||||
memset(command.data, 0, write_buf_size); //maybe not needed here
|
||||
command.data[1] = 'Q';
|
||||
command.data[2] = FigureIndex; // Figure index (0x00-0x0F)
|
||||
command.data[3] = BlockIndex;
|
||||
uint8* output;
|
||||
do {
|
||||
Write(&command);
|
||||
output = Read();
|
||||
} while (output[0] != 'Q');
|
||||
UE_LOG(LogSkyportalIO, VeryVerbose, TEXT("Reading block %d"), BlockIndex);
|
||||
|
||||
if (output[1] == (0x10 + 'Q')) // Successful query
|
||||
{
|
||||
// Extract the 16 bytes of block data
|
||||
for (int i = 3; i < 19; i++)
|
||||
{
|
||||
FigureData.data[BlockIndex][i] = output[i];
|
||||
}
|
||||
}
|
||||
else if (output[0] == 0x01) {
|
||||
FigureData.dataError = true;
|
||||
UE_LOG(LogSkyportalIO, Warning, TEXT("Error querying block %d from figure %d"), BlockIndex, FigureIndex);
|
||||
break;
|
||||
// Query the block from the portal
|
||||
uint8* output = QueryBlock(FigureIndex, BlockIndex);
|
||||
if (output == 0) {
|
||||
--BlockIndex;
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("Query error, reprocessing..."));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify that the block index matches the requested block
|
||||
uint8 ReturnedBlockIndex = output[3];
|
||||
if (ReturnedBlockIndex != BlockIndex)
|
||||
// Copy 16 bytes from the output, starting at the third byte
|
||||
FMemory::Memcpy(TempFigureData->data[BlockIndex], output + 3, FIGURE_BLOCK_SIZE);
|
||||
|
||||
|
||||
// Block 1 is sometimes a duplicate of block 0
|
||||
if (BlockIndex == 1)
|
||||
{
|
||||
FigureData.dataError = true;
|
||||
UE_LOG(LogSkyportalIO, Warning, TEXT("Mismatched block index. Expected %d, got %d"), BlockIndex, ReturnedBlockIndex);
|
||||
break;
|
||||
if (FMemory::Memcmp(TempFigureData->data[1], TempFigureData->data[0], FIGURE_BLOCK_SIZE) == 0)
|
||||
{
|
||||
// Decrement BlockIndex to reprocess block 1 if it's a duplicate of block 0
|
||||
--BlockIndex;
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Block 1 is a duplicate of block 0, reprocessing..."));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy block data into FigureData
|
||||
//memcpy(FigureData.blockdata[BlockIndex], &res.buf[4], FIGURE_BLOCK_SIZE);
|
||||
if (((BlockIndex + 1) % 4 == 0) || BlockIndex < 8)
|
||||
{
|
||||
// Direct copy from data to decryptedData for certain blocks
|
||||
FMemory::Memcpy(TempFigureData->decryptedData[BlockIndex], TempFigureData->data[BlockIndex], FIGURE_BLOCK_SIZE);
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Block %d doesnt need decryption"), BlockIndex);
|
||||
}
|
||||
else {
|
||||
/***** MD5 Hash Calculation *****/
|
||||
// Prepare the hash input buffer
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("DECRYPT -- Starting decryting block %d"), BlockIndex);
|
||||
uint8 hashIn[0x56];
|
||||
FMemory::Memcpy(hashIn, TempFigureData->data[0], 16);
|
||||
FMemory::Memcpy(hashIn + 0x10, TempFigureData->data[1], 16);
|
||||
hashIn[0x20] = BlockIndex;
|
||||
// Append the 35-byte constant
|
||||
FMemory::Memcpy(hashIn + 0x21, HASH_CONST, sizeof(HASH_CONST));
|
||||
|
||||
// Compute the MD5 hash
|
||||
FMD5 MD5;
|
||||
MD5.Update(hashIn, sizeof(hashIn));
|
||||
uint8 key[16]; // MD5 produces 16 bytes
|
||||
MD5.Final(key);
|
||||
|
||||
// AES-128 ECB decryption
|
||||
|
||||
FAES::FAESKey AesKey;
|
||||
FMemory::Memcpy(AesKey.Key, key, 16);
|
||||
|
||||
//TODO: FAES implementation in Unreal is AES-256. We need AES-128
|
||||
//FAES::DecryptData(TempFigureData.decryptedData[BlockIndex], TempFigureData.data[BlockIndex], 16, AesKey);
|
||||
DecryptAES128(TempFigureData->decryptedData[BlockIndex], TempFigureData->data[BlockIndex], key);
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("DECRYPT -- block %d decrypted"), BlockIndex);
|
||||
}
|
||||
UE_LOG(LogSkyportalIO, VeryVerbose, TEXT("block %d readed with success"), BlockIndex);
|
||||
|
||||
}
|
||||
|
||||
// Some verifications should happen. Like if dataError is set.
|
||||
|
||||
return FigureData; // Return the complete figure data
|
||||
return TempFigureData; // Return the complete figure data
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,11 @@ uint32 FPortalStatusChecker::Run()
|
|||
// Main loop of the thread, runs until Stop() is called
|
||||
while (bShouldRun)
|
||||
{
|
||||
USkyPortalSubsystem* subref = Cast<USkyPortalSubsystem>(SkyPortalSubsystemRef);
|
||||
if (subref && !(subref->bShouldPauseRunner)) {
|
||||
// Check the portal status
|
||||
CheckPortalStatus();
|
||||
}
|
||||
|
||||
// Sleep for the specified interval
|
||||
FPlatformProcess::Sleep(CheckInterval);
|
||||
|
@ -46,18 +49,18 @@ void FPortalStatusChecker::CheckPortalStatus()
|
|||
{
|
||||
USkyPortalSubsystem* subref = Cast<USkyPortalSubsystem>(SkyPortalSubsystemRef);
|
||||
USkyPortalIO* PortalHandleRef = subref->PortalHandle;
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Check portal"));
|
||||
UE_LOG(LogSkyportalIO, VeryVerbose, TEXT("Check portal"));
|
||||
if (PortalHandleRef && PortalHandleRef->bPortalReady)
|
||||
{
|
||||
// Call the subsystem function to get portal status
|
||||
uint8* output = PortalHandleRef->Read();
|
||||
EPortalCommand CommandResponse = GetPortalCommandFromChar(output[0]);
|
||||
FigureData figData;
|
||||
//UFigureData figData;
|
||||
switch (CommandResponse)
|
||||
{
|
||||
case S:
|
||||
CurrentStatusData = ParsePortalStatus(output);
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Output Data: %s"), *BytesToHex(output, sizeof(output)));
|
||||
UE_LOG(LogSkyportalIO, VeryVerbose, TEXT("Output Data: %s"), *BytesToHex(output, sizeof(output)));
|
||||
|
||||
//Send delegate when new informations are received
|
||||
if (OldStatusData != CurrentStatusData) {
|
||||
|
@ -72,15 +75,11 @@ void FPortalStatusChecker::CheckPortalStatus()
|
|||
switch (CurrentStatusData.StatusArray[i])
|
||||
{
|
||||
case EFigureStatus::NOT_PRESENT:
|
||||
|
||||
break;
|
||||
case EFigureStatus::PRESENT:
|
||||
//FigureData = ReadFigureBlocks(i);
|
||||
//subref->OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i);
|
||||
break;
|
||||
case EFigureStatus::ADDED:
|
||||
figData = PortalHandleRef->ReadFigureBlocks(i);
|
||||
subref->OnSkylanderAdded.Broadcast(figData.GetFigureID(), i);
|
||||
//figData = PortalHandleRef->ReadFigureBlocks(i);
|
||||
subref->OnSkylanderAdded.Broadcast(00, i);
|
||||
break;
|
||||
case EFigureStatus::REMOVED:
|
||||
subref->OnSkylanderRemoved.Broadcast(00, i);
|
||||
|
@ -98,7 +97,7 @@ void FPortalStatusChecker::CheckPortalStatus()
|
|||
}
|
||||
|
||||
// Do something with the status (log, notify, etc.)
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Portal Status:"));
|
||||
UE_LOG(LogSkyportalIO, VeryVerbose, TEXT("Portal Status:"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,20 +2,23 @@
|
|||
|
||||
#include "SkyPortalSubsystem.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "SkyPortal.h"
|
||||
|
||||
#include "HAL/RunnableThread.h"
|
||||
#include "Misc/ScopeLock.h"
|
||||
#include "SkyPortalFigure.h"
|
||||
#include <Async/TaskGraphInterfaces.h>
|
||||
#include <Async/Async.h>
|
||||
|
||||
|
||||
void USkyPortalSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
|
||||
Super::Initialize(Collection);
|
||||
// Start the status checker thread
|
||||
StatusChecker = new FPortalStatusChecker(this, RunnerInterval);
|
||||
StatusCheckerThread = FRunnableThread::Create(StatusChecker, TEXT("PortalStatusCheckerThread"), 0, TPri_AboveNormal);
|
||||
|
||||
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"));
|
||||
|
||||
}
|
||||
|
@ -118,6 +121,7 @@ SKYPORTAL_API bool USkyPortalSubsystem::RestartRunner()
|
|||
StatusCheckerThread = FRunnableThread::Create(StatusChecker, TEXT("PortalStatusCheckerThread"), 0, TPri_AboveNormal);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Initialized"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -211,14 +215,30 @@ void USkyPortalSubsystem::Sleep(int sleepMs) {
|
|||
|
||||
bool USkyPortalSubsystem::PortalConnect()
|
||||
{
|
||||
if (StatusChecker)
|
||||
{
|
||||
StatusChecker->Stop();
|
||||
StatusCheckerThread->WaitForCompletion();
|
||||
|
||||
delete StatusCheckerThread;
|
||||
delete StatusChecker;
|
||||
StatusChecker = nullptr;
|
||||
StatusCheckerThread = nullptr;
|
||||
}
|
||||
|
||||
PortalHandle = NewObject<USkyPortalIO>();
|
||||
if (IsValid(PortalHandle)) {
|
||||
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;
|
||||
|
@ -233,7 +253,51 @@ bool USkyPortalSubsystem::IsPortalReady()
|
|||
return false;
|
||||
}
|
||||
|
||||
SKYPORTAL_API void USkyPortalSubsystem::PortalMusic(const USoundWave* Sound)
|
||||
void USkyPortalSubsystem::PortalMusic(const USoundWave* Sound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void USkyPortalSubsystem::PortalAnalyzeAsync(const uint8 Index)
|
||||
{
|
||||
bShouldPauseRunner = true;
|
||||
UE_LOG(LogSkyportalIO, Log, TEXT("Starting async figure analysis for index %d..."), Index);
|
||||
|
||||
// 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;
|
||||
}
|
|
@ -1,19 +1,32 @@
|
|||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
//#include "SkyPortalFigure.generated.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SkyPortalFigure.generated.h"
|
||||
|
||||
//Copyright (C) 2010 Activision.All Rights Reserved.
|
||||
static const uint8 HASH_CONST[] = {
|
||||
0x20, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, 0x32, // Copyright (C) 2
|
||||
0x30, 0x31, 0x30, 0x20, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x2E, 0x20, // 010 Activision.
|
||||
0x41, 0x6C, 0x6C, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65, 0x73, 0x65, 0x72, // All Rights Reser
|
||||
0x76, 0x65, 0x64, 0x2E, 0x20 }; // ved.
|
||||
|
||||
#define FIGURE_TOTAL_BLOCKS 64
|
||||
#define FIGURE_BLOCK_SIZE 16
|
||||
|
||||
/*
|
||||
typedef struct {
|
||||
unsigned char blockdata[FIGURE_TOTAL_BLOCKS][FIGURE_BLOCK_SIZE]; bool error;
|
||||
} FigureDataBlock;
|
||||
*/
|
||||
|
||||
UCLASS()
|
||||
class UFigureData : public UObject {
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
class FigureData {
|
||||
public:
|
||||
|
||||
UFigureData();
|
||||
|
||||
// Data Arrays
|
||||
uint8 data[64][16];
|
||||
uint8 decryptedData[64][16];
|
||||
|
@ -32,29 +45,24 @@ public:
|
|||
uint64 FullToyCodeNumber;
|
||||
FString ToyCode;
|
||||
#pragma endregion
|
||||
int16 ID;
|
||||
int16 VariantID;
|
||||
int16 ID; //Skylander ID
|
||||
int16 VariantID; //Skylander Variant ID
|
||||
#pragma region counters
|
||||
uint8 counter1;
|
||||
uint8 couter2;
|
||||
uint8 counter2;
|
||||
#pragma endregion
|
||||
FString Nickname;
|
||||
|
||||
FigureData()
|
||||
{
|
||||
ClearData();
|
||||
}
|
||||
|
||||
|
||||
// Methods
|
||||
UFUNCTION(BlueprintCallable)
|
||||
int32 GetFigureID();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal|Figure"))
|
||||
uint32 GetFigureID();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal|Figure"))
|
||||
uint32 GetFigureIdByIndex(uint32 index);
|
||||
|
||||
UFUNCTION()
|
||||
TArray<uint8> QueryBlock(uint8 characterIndex, uint8 block = 0x00);
|
||||
//UFUNCTION()
|
||||
//TArray<uint8> QueryBlock(uint8 characterIndex, uint8 block = 0x00);
|
||||
|
||||
void ReadData(uint8 index);
|
||||
void ClearData();
|
||||
|
|
|
@ -25,6 +25,8 @@ const int ProductIds[4] = { 0x150, 0x967, 0x1f17 };
|
|||
|
||||
FPortalStatusData ParsePortalStatus(const uint8* StatusResponse);
|
||||
EPortalCommand GetPortalCommandFromChar(unsigned char Char);
|
||||
FString DataToString(const uint8* data);
|
||||
FString OutputToString(const unsigned char* output);
|
||||
|
||||
/* Contain all the data that pass inside hidapi write + the number of bytes in case of successful write
|
||||
*
|
||||
|
@ -62,7 +64,7 @@ public:
|
|||
bool OpenPortalHandle();
|
||||
|
||||
/* Send block to portal. In windows, don't use the hidapi */
|
||||
void Write(FWriteBlock* Block);
|
||||
bool Write(FWriteBlock* Block);
|
||||
|
||||
/* Send raw command to portal */
|
||||
void WriteRaw(const TArray<uint8>* block);
|
||||
|
@ -72,10 +74,13 @@ public:
|
|||
*/
|
||||
uint8* Read();
|
||||
|
||||
UFUNCTION()
|
||||
TArray<uint8> QueryBlock(uint8 characterIndex, uint8 block = 0x00);
|
||||
|
||||
FigureData ReadFigureBlocks(uint8 FigureIndex);
|
||||
/*Trying to read a figure
|
||||
* @return
|
||||
*/
|
||||
unsigned char* QueryBlock(uint8 characterIndex, uint8 block = 0x00);
|
||||
|
||||
UFigureData* ReadFigureBlocks(uint8 FigureIndex);
|
||||
|
||||
/* Close connection to Portal*/
|
||||
void Close();
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "Subsystems/EngineSubsystem.h"
|
||||
#include "SkyPortalRunner.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "SkyPortalFigure.h"
|
||||
#include "SkyPortalIO.h"
|
||||
#include "SkyPortalRunner.h"
|
||||
#include "Subsystems/EngineSubsystem.h"
|
||||
#include "Delegates/DelegateCombinations.h"
|
||||
#include <Containers/Array.h>
|
||||
|
||||
#include "SkyPortalSubsystem.generated.h"
|
||||
|
||||
|
||||
#pragma region Definitions
|
||||
|
||||
//// Delegates
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderAddedDelegate, int32, SkylanderID, int32, Index);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderRemovedDelegate, int32, SkylanderID, int32, Index);
|
||||
|
||||
#pragma endregion
|
||||
|
@ -74,6 +81,12 @@ public:
|
|||
UFUNCTION(BlueprintCallable, meta = (Category = "SkyPortal|NOT FUNCTIONING"))
|
||||
SKYPORTAL_API void PortalMusic(const USoundWave* Sound);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (Category = "SkyPortal|Figure|Debug"))
|
||||
SKYPORTAL_API void PortalAnalyzeAsync(const uint8 index);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (Category = "SkyPortal|Commands|Figure"))
|
||||
SKYPORTAL_API void GetFigureArray();
|
||||
|
||||
/**/
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, meta = (Category = "SkyPortal|Commands"))
|
||||
SKYPORTAL_API FPortalStatusData PortalStatus();
|
||||
|
@ -104,6 +117,9 @@ public:
|
|||
UPROPERTY(BlueprintReadOnly)
|
||||
FPortalStatusData StatusData;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
TArray<UFigureData*> FigureArray;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
float RunnerInterval = 0.01f; //In seconds
|
||||
|
||||
|
@ -127,8 +143,8 @@ public:
|
|||
UPROPERTY()
|
||||
bool bPortalConnected_DEPRECATED = PortalHandle ? PortalHandle->bPortalReady : false;
|
||||
|
||||
|
||||
|
||||
UPROPERTY()
|
||||
bool bShouldPauseRunner;
|
||||
|
||||
|
||||
|
||||
|
@ -140,6 +156,8 @@ private:
|
|||
*/
|
||||
static void Sleep(int sleepMs);
|
||||
|
||||
void PortalAnalyze(const uint8 Index);
|
||||
|
||||
// Pointer to the status checker thread
|
||||
FPortalStatusChecker* StatusChecker;
|
||||
FRunnableThread* StatusCheckerThread;
|
||||
|
|
|
@ -62,8 +62,9 @@ public class SkyPortal : ModuleRules
|
|||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
"Engine",
|
||||
"OpenSSL",
|
||||
"SSL"
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -75,7 +76,7 @@ public class SkyPortal : ModuleRules
|
|||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue