Lucas Peter 2024-09-24 18:24:05 +02:00
parent aed30481ea
commit be72994dc0
No known key found for this signature in database
7 changed files with 302 additions and 34 deletions

View file

@ -0,0 +1,81 @@
#pragma once
#include "SkyPortalFigure.h"
#include "SkyPortalSubsystem.h"
uint32 GetFigureID(const FigureDataBlock& DataBlock)
{
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);
/*
// 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;
};
void FigureData::ReadData(uint8 index)
{
ClearData();
for (uint8 i = 0; i < 64; i++)
{
USkyPortalSubsystem* SkySubsystem = 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()
{
}

View file

@ -0,0 +1,69 @@
#include "SkyPortalIO.h"
#include "Engine/Engine.h"
DEFINE_LOG_CATEGORY(LogHIDApi);
DEFINE_LOG_CATEGORY(LogSkyportalIO);
bool OpenPortalHandle() {
//reset
if (PortalDevice) {
hid_close(PortalDevice);
}
/*
Declare two pointers to hold information about HID devices.
"list" will point to the head of the linked list of devices,
"attributes" will be used to iterate through the list.
*/
struct hid_device_info* list, * attributes;
list = hid_enumerate(0x0, 0x0);
// If `list` is NULL, that means no devices were found or there was an error.
// In this case, print an error message and terminate the program.
if (!list) {
UE_LOG(LogHIDApi, Error, TEXT("No devices found"));
// Get the error message from the HIDAPI
HidError = hid_error(NULL);
UE_LOG(LogHIDApi, Error, TEXT("%s"), *HidError);
return false;
}
attributes = list;
// Iterate through the linked list of devices
int vendorCount = sizeof(VendorIds) / sizeof(VendorIds[0]);
int productCount = sizeof(ProductIds) / sizeof(ProductIds[0]);
while (attributes) {
// Check if the devices match any of vendor_id and product_id
for (int i = 0; i < vendorCount; i++) {
for (int j = 0; j < productCount; j++) {
if (attributes->vendor_id == VendorIds[i] && attributes->product_id == ProductIds[j]) {
UE_LOG(LogHIDApi, Display, TEXT("Portal found"));
UE_LOG(LogHIDApi, Log, TEXT("Vendor ID: 0x%x, Product ID: 0x%x"), attributes->vendor_id, attributes->product_id);
PortalDevice = hid_open(attributes->vendor_id, attributes->product_id, NULL);
if (PortalDevice) {
UE_LOG(LogHIDApi, Display, TEXT("Successful connection to Portal."));
// Free the device list
hid_free_enumeration(list);
//bPortalConnected = true;
return true;
}
break;
}
}
}
// Move to the next device in the list
attributes = attributes->next;
}
// Free the device list
hid_free_enumeration(list);
UE_LOG(LogHIDApi, Error, TEXT("No Portals found"));
return false;
}

View file

@ -5,9 +5,8 @@
#include "HAL/RunnableThread.h"
#include "Misc/ScopeLock.h"
#include "SkyPortalIO.h"
DEFINE_LOG_CATEGORY(LogHIDApi);
DEFINE_LOG_CATEGORY(LogSkyportalIO);
void USkyPortalSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
@ -289,6 +288,7 @@ FPortalStatusData USkyPortalSubsystem::ParsePortalStatus(const RWBlock& Response
FigureStatusArray |= (ResponseBlock.buf[3] << 16); // 3rd byte
FigureStatusArray |= (ResponseBlock.buf[4] << 24); // 4th byte
TStaticArray<EFigureStatus, 16> tempArray;
// For each of the 16 entries, extract the 2-bit status and map it to EFigureStatus
for (int32 i = 0; i < 16; ++i)
{
@ -315,9 +315,11 @@ FPortalStatusData USkyPortalSubsystem::ParsePortalStatus(const RWBlock& Response
}
// Add to the array of figure statuses
PortalStatusData.StatusArray.Add(FigureStatus);
//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.buf[5];
@ -373,19 +375,24 @@ void USkyPortalSubsystem::CheckComplexResponse() {
for (int i = 0; i < CurrentStatusData.StatusArray.Num(); i++) {
if (CurrentStatusData.StatusArray[i] != OldStatusData.StatusArray[i]) {
if (!FalsePositive()) {
if (
//!FalsePositive() //filter conflicting infos
true) {
FigureDataBlock FigureData;
switch (CurrentStatusData.StatusArray[i])
{
case EFigureStatus::NOT_PRESENT:
OnSkylanderRemoved.Broadcast(00, i);
break;
case EFigureStatus::PRESENT:
FigureData = ReadFigureBlocks(i);
OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i);
break;
case EFigureStatus::ADDED:
//get SkylandID
//ReadBlock;
OnSkylanderAdded.Broadcast(01, i);
FigureData = ReadFigureBlocks(i);
OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i);
case EFigureStatus::REMOVED:
OnSkylanderRemoved.Broadcast(01, i)
OnSkylanderRemoved.Broadcast(00, i);
}
}
}
@ -403,10 +410,30 @@ void USkyPortalSubsystem::CheckComplexResponse() {
}
bool USkyPortalSubsystem::FalsePositive()
TArray<uint8> USkyPortalSubsystem::QueryBlock(uint8 characterIndex, uint8 block)
{
TArray<uint8> QueryCommand;
QueryCommand.SetNum(0x21);
QueryCommand[1] = 'Q';
QueryCommand[2] = characterIndex;
QueryCommand[3] = block;
TArray<uint8> Output;
do {
portalConnection->Write(QueryCommand);
output = portalConnection->Read();
} while (output[0] != 'Q' || (output[1] % 0x10 != characterIndex && output[1] != 0x01) || output[2] != block);
return output;
}
bool USkyPortalSubsystem::FalsePositive() const
{
int dif = FMath::Abs(CurrentStatusData.Counter - OldStatusData.Counter);
return (dif <= 2 || dif > 254);
return (dif <= 2 || dif >= 254);
}

View file

@ -1,3 +1,60 @@
#pragma once
#include "CoreMinimal.h"
//#include "SkyPortalFigure.generated.h"
#include "SkyPortalFigure.generated.h"
#define FIGURE_TOTAL_BLOCKS 64
#define FIGURE_BLOCK_SIZE 16
typedef struct {
unsigned char blockdata[FIGURE_TOTAL_BLOCKS][FIGURE_BLOCK_SIZE]; bool error;
} FigureDataBlock;
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal|Figure"))
uint32 GetFigureID(const FigureDataBlock& DataBlock);
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal|Figure"))
uint32 GetFigureIdByIndex(uint32 index);
class FigureData {
public:
// Data Arrays
uint8 data[64][16];
uint8 decryptedData[64][16];
// Properties
#pragma region manufacturer
uint32 NUID; // should under no circumstances be corrupted. 4-byte
uint8 BCC; //Block Check Character. When this BCC does not match the NUID of the tag, the tag ceases to function. stored in block 0 at offset 0x4
uint8 SAK = 0x81; //needs to be set to allow for the tag to be read correctly
uint16 ATQA; //always set to 0x01 0x0F
FString ProductionYear; //last 2 digits of the year that the tag was manufactured as Binary-coded decimal
#pragma endregion
int16 ID;
#pragma region Toy code
uint32 ToyCodeNumber1, ToyCodeNumber2;
uint64 FullToyCodeNumber;
FString ToyCode;
#pragma endregion
int16 VariantID;
#pragma region counters
uint8 counter1;
uint8 couter2;
#pragma endregion
FString Nickname;
FigureData()
{
ClearData();
}
// Methods
void ReadData(uint8 index);
void ClearData();
uint8 GetByte(int block, int offset);
void SetByte(int block, int offset, uint8 value);
uint16 GetShort(int block, int offset);
uint32 GetUInt(int block, int offset);
void Dump(bool decrypted, FString filePath);
};

View file

@ -0,0 +1,33 @@
#pragma once
/*
* This is the bridge between hidapi and unreal and contains all the rawdata of the portal
*/
#include "CoreMinimal.h"
#include "hidapi.h"
#include "SkyPortalIO.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHIDApi, Log, All);
DECLARE_LOG_CATEGORY_EXTERN(LogSkyportalIO, Log, All);
UCLASS(MinimalAPI)
class SkyPortalIO : public UObjectBase
{
GENERATED_BODY()
public:
SkyPortalIO()
{
OpenPortalHandle();
}
private:
bool OpenPortalHandle();
//Portal ref used in the subsystem
hid_device* PortalDevice;
};

View file

@ -1,6 +1,5 @@
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/EngineSubsystem.h"
#include "hidapi.h"
#include "HAL/Runnable.h"
@ -46,7 +45,7 @@ struct FPortalStatusData
GENERATED_USTRUCT_BODY()
// Array of statuses
UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure")
UPROPERTY(BlueprintReadOnly, EditFixedSize, Category = "SkyPortal|Figure", meta = (EditFixedOrder))
TArray<EFigureStatus> StatusArray;
// timestamp.
@ -59,6 +58,14 @@ struct FPortalStatusData
UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure")
bool bIsReady;
explicit FPortalStatusData(uint8 ArraySize = 16, EFigureStatus DefaultStatus = EFigureStatus::NOT_PRESENT) :
Counter(0), // Utilisation correcte de l'initialisation directe
bIsReady(true) // Utilisation correcte de l'initialisation directe
{
// Initialisation du tableau StatusArray avec 16 éléments par défaut
StatusArray.Init(DefaultStatus, ArraySize);
}
// Overload the == operator
bool operator==(const FPortalStatusData& Other) const
{
@ -81,19 +88,12 @@ struct FPortalStatusData
#define rw_buf_size 0x21
#define TIMEOUT 30000
#define DEBUG true
#define FIGURE_TOTAL_BLOCKS 64
#define FIGURE_BLOCK_SIZE 16
typedef struct {
unsigned char buf[rw_buf_size]; int BytesTransferred;
} RWBlock;
typedef struct {
unsigned char blockdata[FIGURE_TOTAL_BLOCKS][FIGURE_BLOCK_SIZE]; bool error;
} FigureDataBlock;
DECLARE_LOG_CATEGORY_EXTERN(LogHIDApi, Log, All);
DECLARE_LOG_CATEGORY_EXTERN(LogSkyportalIO, Log, All);
//// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderAddedDelegate, int32, SkylanderID, int32, Index);
@ -184,7 +184,7 @@ public:
* @param PortalSide The actors to record
* @param BlendTime Blend between current color and NextColor, in milliseconds
*/
UFUNCTION(BlueprintCallable, CallInEditor, meta = (AutoCreateRefTerm = "NextColor", Category = "SkyPortal|Cosmetic"))
UFUNCTION(BlueprintCallable, CallInEditor, meta = (AutoCreateRefTerm = "NextColor", Category = "SkyPortal|Cosmetic", HideAlphaChannel))
SKYPORTAL_API void ChangePortalColorside(const FLinearColor& NextColor = FLinearColor::Green, const EPortalSide PortalSide = EPortalSide::BOTH, const float BlendTime = 500.0f);
@ -215,7 +215,8 @@ public:
bool CheckResponse(RWBlock*, char);
void CheckComplexResponse();
UFUNCTION()
TArray<uint8> QueryBlock(uint8 characterIndex, uint8 block = 0x00);
private:
@ -237,7 +238,7 @@ private:
bool OpenPortalHandle();
void Write(RWBlock*);
FigureDataBlock ReadFigureBlocks(uint8 FigureIndex);
bool FalsePositive();
bool FalsePositive() const;
// Pointer to the status checker thread
FPortalStatusChecker* StatusChecker;