diff --git a/Source/SkyPortal/Private/SkyPortal.cpp b/Source/SkyPortal/Private/SkyPortal.cpp index 162c591..4724a77 100644 --- a/Source/SkyPortal/Private/SkyPortal.cpp +++ b/Source/SkyPortal/Private/SkyPortal.cpp @@ -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()) { + GEngine->GetEngineSubsystem()->Deinitialize(); + } } #undef LOCTEXT_NAMESPACE diff --git a/Source/SkyPortal/Private/SkyPortalFigure.cpp b/Source/SkyPortal/Private/SkyPortalFigure.cpp index 1b00736..0fb8652 100644 --- a/Source/SkyPortal/Private/SkyPortalFigure.cpp +++ b/Source/SkyPortal/Private/SkyPortalFigure.cpp @@ -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(); - TArray 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() { } diff --git a/Source/SkyPortal/Private/SkyPortalIO.cpp b/Source/SkyPortal/Private/SkyPortalIO.cpp index 9248806..a3a4b3e 100644 --- a/Source/SkyPortal/Private/SkyPortalIO.cpp +++ b/Source/SkyPortal/Private/SkyPortalIO.cpp @@ -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* 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 USkyPortalIO::QueryBlock(uint8 characterIndex, uint8 block) +uint8* USkyPortalIO::QueryBlock(uint8 FigureIndex, uint8 BlockIndex) { - return TArray(); + 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(); + 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); + + // Query the block from the portal + uint8* output = QueryBlock(FigureIndex, BlockIndex); + if (output == 0) { + --BlockIndex; + UE_LOG(LogSkyportalIO, Error, TEXT("Query error, reprocessing...")); + continue; + } + + // Copy 16 bytes from the output, starting at the third byte + FMemory::Memcpy(TempFigureData->data[BlockIndex], output + 3, FIGURE_BLOCK_SIZE); - if (output[1] == (0x10 + 'Q')) // Successful query + + // Block 1 is sometimes a duplicate of block 0 + if (BlockIndex == 1) { - // Extract the 16 bytes of block data - for (int i = 3; i < 19; i++) + if (FMemory::Memcmp(TempFigureData->data[1], TempFigureData->data[0], FIGURE_BLOCK_SIZE) == 0) { - FigureData.data[BlockIndex][i] = output[i]; + // 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; } } - else if (output[0] == 0x01) { - FigureData.dataError = true; - UE_LOG(LogSkyportalIO, Warning, TEXT("Error querying block %d from figure %d"), BlockIndex, FigureIndex); - break; - } - // Verify that the block index matches the requested block - uint8 ReturnedBlockIndex = output[3]; - if (ReturnedBlockIndex != BlockIndex) + if (((BlockIndex + 1) % 4 == 0) || BlockIndex < 8) { - FigureData.dataError = true; - UE_LOG(LogSkyportalIO, Warning, TEXT("Mismatched block index. Expected %d, got %d"), BlockIndex, ReturnedBlockIndex); - break; + // 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); - // Copy block data into FigureData - //memcpy(FigureData.blockdata[BlockIndex], &res.buf[4], FIGURE_BLOCK_SIZE); } // Some verifications should happen. Like if dataError is set. - return FigureData; // Return the complete figure data + return TempFigureData; // Return the complete figure data } + diff --git a/Source/SkyPortal/Private/SkyPortalRunner.cpp b/Source/SkyPortal/Private/SkyPortalRunner.cpp index fce6818..5fa3c4f 100644 --- a/Source/SkyPortal/Private/SkyPortalRunner.cpp +++ b/Source/SkyPortal/Private/SkyPortalRunner.cpp @@ -18,8 +18,11 @@ uint32 FPortalStatusChecker::Run() // Main loop of the thread, runs until Stop() is called while (bShouldRun) { - // Check the portal status - CheckPortalStatus(); + USkyPortalSubsystem* subref = Cast(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(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:")); } } diff --git a/Source/SkyPortal/Private/SkyPortalSubsystem.cpp b/Source/SkyPortal/Private/SkyPortalSubsystem.cpp index 9d9ae34..7a59cd1 100644 --- a/Source/SkyPortal/Private/SkyPortalSubsystem.cpp +++ b/Source/SkyPortal/Private/SkyPortalSubsystem.cpp @@ -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 +#include 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(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(); - 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; +} \ No newline at end of file diff --git a/Source/SkyPortal/Public/SkyPortalDefinitions.h b/Source/SkyPortal/Public/SkyPortalDefinitions.h index fda362b..5e87a14 100644 --- a/Source/SkyPortal/Public/SkyPortalDefinitions.h +++ b/Source/SkyPortal/Public/SkyPortalDefinitions.h @@ -80,4 +80,4 @@ struct FPortalStatusData { return !(*this == Other); } -}; \ No newline at end of file +}; diff --git a/Source/SkyPortal/Public/SkyPortalFigure.h b/Source/SkyPortal/Public/SkyPortalFigure.h index b597203..8b39588 100644 --- a/Source/SkyPortal/Public/SkyPortalFigure.h +++ b/Source/SkyPortal/Public/SkyPortalFigure.h @@ -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 QueryBlock(uint8 characterIndex, uint8 block = 0x00); + //UFUNCTION() + //TArray QueryBlock(uint8 characterIndex, uint8 block = 0x00); void ReadData(uint8 index); void ClearData(); diff --git a/Source/SkyPortal/Public/SkyPortalIO.h b/Source/SkyPortal/Public/SkyPortalIO.h index c94683d..c6287c8 100644 --- a/Source/SkyPortal/Public/SkyPortalIO.h +++ b/Source/SkyPortal/Public/SkyPortalIO.h @@ -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* block); @@ -72,10 +74,13 @@ public: */ uint8* Read(); - UFUNCTION() - TArray 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(); diff --git a/Source/SkyPortal/Public/SkyPortalSubsystem.h b/Source/SkyPortal/Public/SkyPortalSubsystem.h index ba065a1..a79f543 100644 --- a/Source/SkyPortal/Public/SkyPortalSubsystem.h +++ b/Source/SkyPortal/Public/SkyPortalSubsystem.h @@ -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 + #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 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; diff --git a/Source/SkyPortal/SkyPortal.Build.cs b/Source/SkyPortal/SkyPortal.Build.cs index 60e8bb3..d220ca2 100644 --- a/Source/SkyPortal/SkyPortal.Build.cs +++ b/Source/SkyPortal/SkyPortal.Build.cs @@ -62,9 +62,10 @@ 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 ... + } );