diff --git a/Source/SkyPortal/Private/SkyPortal.cpp b/Source/SkyPortal/Private/SkyPortal.cpp index 03542a6..162c591 100644 --- a/Source/SkyPortal/Private/SkyPortal.cpp +++ b/Source/SkyPortal/Private/SkyPortal.cpp @@ -1,6 +1,7 @@ // Copyright Epic Games, Inc. All Rights Reserved. #include "SkyPortal.h" +#include "SkyPortalSubsystem.h" #define LOCTEXT_NAMESPACE "FSkyPortalModule" diff --git a/Source/SkyPortal/Private/SkyPortalFigure.cpp b/Source/SkyPortal/Private/SkyPortalFigure.cpp index 04945ff..1b00736 100644 --- a/Source/SkyPortal/Private/SkyPortalFigure.cpp +++ b/Source/SkyPortal/Private/SkyPortalFigure.cpp @@ -2,20 +2,27 @@ #include "SkyPortalFigure.h" -uint32 GetFigureID(const FigureDataBlock& DataBlock) + +uint32 FigureData::GetFigureID() { - int16_t OutFigureID; - // Figure ID is stored in Block 0, bytes 0 to 3 (32-bit integer, little-endian) - OutFigureID = DataBlock.blockdata[0][0] | - (DataBlock.blockdata[0][1] << 8) | - (DataBlock.blockdata[0][2] << 16) | - (DataBlock.blockdata[0][3] << 24); + 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 + (data[1][1] << 8); // Most significant byte, shifted left by 8 bits + + ID = OutFigureID;// Return the 16-bit figure ID + return OutFigureID; + } + else { return ID; } /* // Variant ID is stored in Block 0, bytes 4 to 5 (16-bit integer, little-endian) OutVariantID = (DataBlock.blockdata[0][5] << 8) | DataBlock.blockdata[0][4]; */ - return OutFigureID; + + }; /* diff --git a/Source/SkyPortal/Private/SkyPortalIO.cpp b/Source/SkyPortal/Private/SkyPortalIO.cpp index ec0e895..9248806 100644 --- a/Source/SkyPortal/Private/SkyPortalIO.cpp +++ b/Source/SkyPortal/Private/SkyPortalIO.cpp @@ -263,3 +263,58 @@ FPortalStatusData ParsePortalStatus(const uint8* StatusResponse) return result; } + + + +FigureData USkyPortalIO::ReadFigureBlocks(uint8 FigureIndex) +{ + + FigureData FigureData; + FigureData.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'); + + 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; + } + + // Verify that the block index matches the requested block + uint8 ReturnedBlockIndex = output[3]; + if (ReturnedBlockIndex != BlockIndex) + { + FigureData.dataError = true; + UE_LOG(LogSkyportalIO, Warning, TEXT("Mismatched block index. Expected %d, got %d"), BlockIndex, ReturnedBlockIndex); + break; + } + + // 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 +} diff --git a/Source/SkyPortal/Private/SkyPortalRunner.cpp b/Source/SkyPortal/Private/SkyPortalRunner.cpp index dc22839..fce6818 100644 --- a/Source/SkyPortal/Private/SkyPortalRunner.cpp +++ b/Source/SkyPortal/Private/SkyPortalRunner.cpp @@ -52,6 +52,7 @@ void FPortalStatusChecker::CheckPortalStatus() // Call the subsystem function to get portal status uint8* output = PortalHandleRef->Read(); EPortalCommand CommandResponse = GetPortalCommandFromChar(output[0]); + FigureData figData; switch (CommandResponse) { case S: @@ -71,15 +72,15 @@ void FPortalStatusChecker::CheckPortalStatus() switch (CurrentStatusData.StatusArray[i]) { case EFigureStatus::NOT_PRESENT: - subref->OnSkylanderRemoved.Broadcast(00, i); + break; case EFigureStatus::PRESENT: //FigureData = ReadFigureBlocks(i); //subref->OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i); break; case EFigureStatus::ADDED: - //FigureData = ReadFigureBlocks(i); - //subref->OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i); + figData = PortalHandleRef->ReadFigureBlocks(i); + subref->OnSkylanderAdded.Broadcast(figData.GetFigureID(), i); break; case EFigureStatus::REMOVED: subref->OnSkylanderRemoved.Broadcast(00, i); diff --git a/Source/SkyPortal/Private/SkyPortalSubsystem.cpp b/Source/SkyPortal/Private/SkyPortalSubsystem.cpp index de5505e..0c57ea6 100644 --- a/Source/SkyPortal/Private/SkyPortalSubsystem.cpp +++ b/Source/SkyPortal/Private/SkyPortalSubsystem.cpp @@ -75,8 +75,12 @@ void USkyPortalSubsystem::PortalReady() output = PortalHandle->Read(); } while (output[0] != 'R'); - uint16 _PortalId = ((output[1] << 8) + output[2]); - PortalId = _PortalId; //Need a conversion as uint16 is not supported in BP + unsigned char _PortalId[2] = { output[1],output[2] }; + int32 BE_PortalId = (_PortalId[0] << 8) | _PortalId[1]; // Big-endian format + uint32 BEU_PortalId = (_PortalId[0] << 8) | _PortalId[1]; // Big-endian format unsigned + int32 LE_PortalId = (_PortalId[1] << 8) | _PortalId[0]; //little endian + uint32 LEU_PortalId = (_PortalId[1] << 8) | _PortalId[0]; //little endian unsigned + PortalId = LE_PortalId; //Need a conversion as uint16 is not supported in BP } @@ -181,60 +185,6 @@ void USkyPortalSubsystem::ChangePortalColorSide(const FLinearColor& Color, const - -FPortalStatusData USkyPortalSubsystem::OLDParsePortalStatus(const FWriteBlock& ResponseBlock) -{ - FPortalStatusData PortalStatusData; - - // Parse the figure status array (little-endian 32-bit integer) - uint32 FigureStatusArray = 0; - // Reading the 32-bit integer (character status array) from the buffer - FigureStatusArray |= ResponseBlock.data[1]; // 1st byte - FigureStatusArray |= (ResponseBlock.data[2] << 8); // 2nd byte - FigureStatusArray |= (ResponseBlock.data[3] << 16); // 3rd byte - FigureStatusArray |= (ResponseBlock.data[4] << 24); // 4th byte - - TStaticArray tempArray; - // For each of the 16 entries, extract the 2-bit status and map it to EFigureStatus - for (int32 i = 0; i < 16; ++i) - { - uint8 StatusBits = (FigureStatusArray >> (i * 2)) & 0b11; // Extract 2 bits - EFigureStatus FigureStatus; - - switch (StatusBits) - { - case 0b00: - FigureStatus = EFigureStatus::NOT_PRESENT; - break; - case 0b01: - FigureStatus = EFigureStatus::PRESENT; - break; - case 0b11: - FigureStatus = EFigureStatus::ADDED; - break; - case 0b10: - FigureStatus = EFigureStatus::REMOVED; - break; - default: - FigureStatus = EFigureStatus::NOT_PRESENT; // Default case - break; - } - - // Add to the array of figure statuses - //PortalStatusData.StatusArray.Insert(FigureStatus, i); - tempArray[i] = FigureStatus; - } - PortalStatusData.StatusArray.SetNum(0); - PortalStatusData.StatusArray.Append(tempArray); - // The next byte is the response counter - PortalStatusData.Counter = ResponseBlock.data[5]; - - // The last byte is the boolean indicating whether the portal is ready - PortalStatusData.bIsReady = ResponseBlock.data[6] != 0; // 0 means not ready, non-zero means ready - - return PortalStatusData; -} - void USkyPortalSubsystem::Sleep(int sleepMs) { FPlatformProcess::Sleep(sleepMs * 0.0001); } @@ -268,69 +218,3 @@ SKYPORTAL_API void USkyPortalSubsystem::PortalMusic(const USoundWave* Sound) { return; } - - -/* -FigureDataBlock USkyPortalSubsystem::ReadFigureBlocks(uint8 FigureIndex) -{ - FigureDataBlock FigureData; - FigureData.error = false; // Initialize error flag - - FWriteBlock req, res; - - // Loop over all 64 blocks - for (uint8 BlockIndex = 0; BlockIndex < FIGURE_TOTAL_BLOCKS; ++BlockIndex) - { - // Prepare the request buffer - memset(command.data, 0, rw_buf_size); - command.data[1] = 'Q'; // Command character - command.data[2] = FigureIndex; // Figure index (0x00 to 0x0F) - command.data[3] = BlockIndex; // Block index (0x00 to 0x3F) - - // Send the request - Write(&req); - - // Read the response - int BuffResponse = hid_read_timeout(PortalDevice, res.buf, rw_buf_size, TIMEOUT); - - // Check if the response was received successfully - if (BuffResponse < rw_buf_size) - { - FigureData.error = true; // Mark error flag - UE_LOG(LogSkyportalIO, Warning, TEXT("Error receiving data for block %d of figure %d"), BlockIndex, FigureIndex); - break; - } - - // Parse the response status - uint8 StatusByte = res.buf[2]; - uint8 ReturnedBlockIndex = res.buf[3]; - - // Check for errors in the status byte - if (StatusByte == 0x01) // Error in response - { - FigureData.error = true; - UE_LOG(LogSkyportalIO, Warning, TEXT("Error reading block %d for figure %d"), BlockIndex, FigureIndex); - break; - } - else if (StatusByte != (0x10 + 'Q')) // Unexpected status byte - { - FigureData.error = true; - UE_LOG(LogSkyportalIO, Warning, TEXT("Unexpected status byte 0x%02X for block %d"), StatusByte, BlockIndex); - break; - } - - // Verify that the block index matches the requested block - if (ReturnedBlockIndex != BlockIndex) - { - FigureData.error = true; - UE_LOG(LogSkyportalIO, Warning, TEXT("Mismatched block index. Expected %d, got %d"), BlockIndex, ReturnedBlockIndex); - break; - } - - // Copy block data into FigureData - memcpy(FigureData.blockdata[BlockIndex], &res.buf[4], FIGURE_BLOCK_SIZE); - } - - return FigureData; // Return the complete figure data -} -*/ \ No newline at end of file diff --git a/Source/SkyPortal/Public/SkyPortal.h b/Source/SkyPortal/Public/SkyPortal.h index 279d11e..3b4fe94 100644 --- a/Source/SkyPortal/Public/SkyPortal.h +++ b/Source/SkyPortal/Public/SkyPortal.h @@ -4,7 +4,6 @@ #include "CoreMinimal.h" #include "Modules/ModuleManager.h" -#include "SkyPortalSubsystem.h" class FSkyPortalModule : public IModuleInterface { diff --git a/Source/SkyPortal/Public/SkyPortalDefinitions.h b/Source/SkyPortal/Public/SkyPortalDefinitions.h new file mode 100644 index 0000000..fda362b --- /dev/null +++ b/Source/SkyPortal/Public/SkyPortalDefinitions.h @@ -0,0 +1,83 @@ +#pragma once + +#include "CoreMinimal.h" +#include "SkyPortalDefinitions.generated.h" + +// This file is not supposed to have a c++ counterpart. Use it as a library + + +UENUM(BlueprintType) +enum class EFigureStatus : uint8 +{ + NOT_PRESENT = 0b00 UMETA(DisplayName = "Not Present"), + PRESENT = 0b01 UMETA(DisplayName = "Present"), + ADDED = 0b11 UMETA(DisplayName = "Added"), + REMOVED = 0b10 UMETA(DisplayName = "Removed") +}; + + +UENUM(BlueprintType) +enum EPortalSide { + LEFT UMETA(DisplayName = "Left side"), + RIGHT UMETA(DisplayName = "Right side"), + BOTH UMETA(DisplayName = "Both left and right"), + TRAP UMETA(DisplayName = "Trap") +}; + +UENUM(BlueprintType) +enum EPortalCommand { + A UMETA(DisplayName = "Activate"), + C UMETA(DisplayName = "Color"), + J UMETA(DisplayName = "Advanced color"), + L UMETA(DisplayName = "Trap color"), + M UMETA(DisplayName = "Music"), + Q UMETA(DisplayName = "Query"), + R UMETA(DisplayName = "Ready"), + S UMETA(DisplayName = "Status") +}; + +USTRUCT(BlueprintType) +struct FPortalStatusData +{ + GENERATED_BODY() + + // Array of statuses + UPROPERTY(BlueprintReadOnly, EditFixedSize, Category = "SkyPortal|Figure", meta = (EditFixedOrder)) + TArray StatusArray; + + // timestamp. + // only one byte long. This means that after the value 0xFF, it overflows back to 0x00. + // Since these are so far apart, it can be assumed that 0x00 is newer than anything in the range 0xF0 - 0xFF. + UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure") + uint8 Counter; + + // Should always be true + UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure") + bool bIsReady; + + explicit FPortalStatusData(uint8 ArraySize = 16, EFigureStatus DefaultStatus = EFigureStatus::NOT_PRESENT) : + Counter(0), + bIsReady(true) + { + // Initialisation du tableau StatusArray avec 16 éléments par défaut + StatusArray.Init(DefaultStatus, ArraySize); + } + + // Overload the == operator + // Different only between + bool operator==(const FPortalStatusData& Other) const + { + if (bIsReady == Other.bIsReady && Counter == Other.Counter) { + if (StatusArray == Other.StatusArray) { + return true; + } + } + return false; + } + + // Overload the != operator + bool operator!=(const FPortalStatusData& Other) const + { + return !(*this == Other); + } +}; \ No newline at end of file diff --git a/Source/SkyPortal/Public/SkyPortalFigure.h b/Source/SkyPortal/Public/SkyPortalFigure.h index 5d6909e..b597203 100644 --- a/Source/SkyPortal/Public/SkyPortalFigure.h +++ b/Source/SkyPortal/Public/SkyPortalFigure.h @@ -3,15 +3,6 @@ //#include "SkyPortalFigure.generated.h" -UENUM(BlueprintType) -enum class EFigureStatus : uint8 -{ - NOT_PRESENT = 0b00 UMETA(DisplayName = "Not Present"), - PRESENT = 0b01 UMETA(DisplayName = "Present"), - ADDED = 0b11 UMETA(DisplayName = "Added"), - REMOVED = 0b10 UMETA(DisplayName = "Removed") -}; - #define FIGURE_TOTAL_BLOCKS 64 #define FIGURE_BLOCK_SIZE 16 @@ -26,6 +17,7 @@ public: // Data Arrays uint8 data[64][16]; uint8 decryptedData[64][16]; + bool dataError; // Properties #pragma region manufacturer @@ -56,7 +48,7 @@ public: // Methods UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal|Figure")) - uint32 GetFigureID(const FigureDataBlock& DataBlock); + uint32 GetFigureID(); UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal|Figure")) uint32 GetFigureIdByIndex(uint32 index); @@ -71,4 +63,6 @@ public: uint16 GetShort(int block, int offset); uint32 GetUInt(int block, int offset); void Dump(bool decrypted, FString filePath); -}; \ No newline at end of file +}; + + diff --git a/Source/SkyPortal/Public/SkyPortalIO.h b/Source/SkyPortal/Public/SkyPortalIO.h index 54a31c7..c94683d 100644 --- a/Source/SkyPortal/Public/SkyPortalIO.h +++ b/Source/SkyPortal/Public/SkyPortalIO.h @@ -4,6 +4,7 @@ */ #include "CoreMinimal.h" #include "hidapi.h" +#include "SkyPortalDefinitions.h" #include "SkyPortalFigure.h" #include "SkyPortalIO.generated.h" @@ -19,63 +20,6 @@ constexpr auto TIMEOUT = 30000; //milliseconds const int VendorIds[4] = { 0x12ba, 0x54c, 0x1430, 0x1430 }; const int ProductIds[4] = { 0x150, 0x967, 0x1f17 }; -UENUM(BlueprintType) -enum EPortalCommand { - A UMETA(DisplayName = "Activate"), - C UMETA(DisplayName = "Color"), - J UMETA(DisplayName = "Advanced color"), - L UMETA(DisplayName = "Trap color"), - M UMETA(DisplayName = "Music"), - Q UMETA(DisplayName = "Query"), - R UMETA(DisplayName = "Ready"), - S UMETA(DisplayName = "Status") -}; - -USTRUCT(BlueprintType) -struct FPortalStatusData -{ - GENERATED_BODY() - - // Array of statuses - UPROPERTY(BlueprintReadOnly, EditFixedSize, Category = "SkyPortal|Figure", meta = (EditFixedOrder)) - TArray StatusArray; - - // timestamp. - // only one byte long. This means that after the value 0xFF, it overflows back to 0x00. - // Since these are so far apart, it can be assumed that 0x00 is newer than anything in the range 0xF0 - 0xFF. - UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure") - uint8 Counter; - - // Should always be true - UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure") - bool bIsReady; - - explicit FPortalStatusData(uint8 ArraySize = 16, EFigureStatus DefaultStatus = EFigureStatus::NOT_PRESENT) : - Counter(0), - bIsReady(true) - { - // Initialisation du tableau StatusArray avec 16 éléments par défaut - StatusArray.Init(DefaultStatus, ArraySize); - } - - // Overload the == operator - // Different only between - bool operator==(const FPortalStatusData& Other) const - { - if (bIsReady == Other.bIsReady && Counter == Other.Counter) { - if (StatusArray == Other.StatusArray) { - return true; - } - } - return false; - } - - // Overload the != operator - bool operator!=(const FPortalStatusData& Other) const - { - return !(*this == Other); - } -}; #pragma endregion @@ -83,7 +27,7 @@ FPortalStatusData ParsePortalStatus(const uint8* StatusResponse); EPortalCommand GetPortalCommandFromChar(unsigned char Char); /* Contain all the data that pass inside hidapi write + the number of bytes in case of successful write -* +* * @param data all the block data, should be 0x21 sized * @param BytesTransferred In case of successful write, print the number of byte transfered. Could be used for verifications */ @@ -131,6 +75,8 @@ public: UFUNCTION() TArray QueryBlock(uint8 characterIndex, uint8 block = 0x00); + FigureData ReadFigureBlocks(uint8 FigureIndex); + /* Close connection to Portal*/ void Close(); diff --git a/Source/SkyPortal/Public/SkyPortalSubsystem.h b/Source/SkyPortal/Public/SkyPortalSubsystem.h index 95baa5c..5b98595 100644 --- a/Source/SkyPortal/Public/SkyPortalSubsystem.h +++ b/Source/SkyPortal/Public/SkyPortalSubsystem.h @@ -3,27 +3,10 @@ #include "Subsystems/EngineSubsystem.h" #include "SkyPortalRunner.h" #include "SkyPortalIO.h" - - #include "SkyPortalSubsystem.generated.h" #pragma region Definitions -UENUM(BlueprintType) -enum EPortalSide { - LEFT UMETA(DisplayName = "Left side"), - RIGHT UMETA(DisplayName = "Right side"), - BOTH UMETA(DisplayName = "Both left and right"), - TRAP UMETA(DisplayName = "Trap") -}; - - - - - - - - //// Delegates DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderAddedDelegate, int32, SkylanderID, int32, Index); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderRemovedDelegate, int32, SkylanderID, int32, Index); @@ -95,7 +78,7 @@ public: UFUNCTION(BlueprintCallable, CallInEditor, meta = (Category = "SkyPortal|Commands")) SKYPORTAL_API FPortalStatusData PortalStatus(); - + /* Change the color of the portal, can separate side and even trap ligth @@ -106,7 +89,7 @@ public: UFUNCTION(BlueprintCallable, CallInEditor, meta = (AutoCreateRefTerm = "NextColor", Category = "SkyPortal|Commands|Cosmetic", HideAlphaChannel)) SKYPORTAL_API void ChangePortalColorSide(const FLinearColor& NextColor = FLinearColor::Green, const EPortalSide PortalSide = EPortalSide::BOTH, const float BlendTime = 500.0f); - + // Blueprint-assignable event property UPROPERTY(BlueprintAssignable, Category = "SkyPortal|Skylander") @@ -146,22 +129,12 @@ public: private: - - // deprecated - FPortalStatusData OLDParsePortalStatus(const FWriteBlock& ResponseBlock); // deprecated - - EPortalCommand GetPortalCommandFromChar(unsigned char Char); // deprecated - - - /* Sleep the system * * @param sleepMs Sleep time (in milliseconds) */ static void Sleep(int sleepMs); - bool FalsePositive() const; - // Pointer to the status checker thread FPortalStatusChecker* StatusChecker; FRunnableThread* StatusCheckerThread;