39 files changed, 4 insertions(+), 1982 deletions(-)
M app/build.gradle.kts
M gradle/libs.versions.toml
M settings.gradle.kts
D vb-nfc-reader/.gitignore
D vb-nfc-reader/.idea/.gitignore
D vb-nfc-reader/.idea/caches/deviceStreaming.xml
D vb-nfc-reader/.idea/gradle.xml
D vb-nfc-reader/.idea/misc.xml
D vb-nfc-reader/.idea/runConfigurations.xml
D vb-nfc-reader/build.gradle.kts
D vb-nfc-reader/consumer-rules.pro
D vb-nfc-reader/proguard-rules.pro
D vb-nfc-reader/src/androidTest/java/com/github/cfogrady/vbnfc/ExampleInstrumentedTest.kt
D vb-nfc-reader/src/main/AndroidManifest.xml
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/ByteManipulation.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/ChecksumCalculator.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/CryptographicTransformer.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/NfcDataTranslator.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/NfcDataTranslatorFactory.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/TagCommunicator.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/Utils.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcCharacter.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDataFactory.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDataTranslator.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDevice.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/FirmwareVersion.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/DeviceSubType.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/DeviceType.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcCharacter.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcData.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcDevice.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcHeader.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/vb/VBNfcDataTranslator.kt
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/vb/VBNfcHeader.kt
D vb-nfc-reader/src/main/res/values/arrays.xml
D vb-nfc-reader/src/main/res/values/strings.xml
D vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/CryptographicTransformerHelper.kt
D vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/CryptographicTransformerTest.kt
D vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/be/BENfcDataTranslatorTest.kt
M app/build.gradle.kts => app/build.gradle.kts +1 -1
@@ 42,7 42,7 @@ android {
dependencies {
implementation(libs.androidx.room.runtime)
- implementation(project(":vb-nfc-reader"))
+ implementation(libs.vb.nfc.reader)
ksp(libs.androidx.room.compiler)
annotationProcessor(libs.androidx.room.compiler)
implementation(libs.androidx.core.ktx)
M gradle/libs.versions.toml => gradle/libs.versions.toml +2 -0
@@ 9,6 9,7 @@ lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024.04.01"
roomRuntime = "2.6.1"
+vbNfcReader = "0.1.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ 27,6 28,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
+vb-nfc-reader = { module = "com.github.cfogrady:vb-nfc-reader", version.ref = "vbNfcReader" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
M settings.gradle.kts => settings.gradle.kts +1 -1
@@ 14,6 14,7 @@ pluginManagement {
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
+ mavenLocal()
google()
mavenCentral()
}
@@ 21,4 22,3 @@ dependencyResolutionManagement {
rootProject.name = "VBHelper"
include(":app")
-include(":vb-nfc-reader")
D vb-nfc-reader/.gitignore => vb-nfc-reader/.gitignore +0 -1
@@ 1,1 0,0 @@
-/build>
\ No newline at end of file
D vb-nfc-reader/.idea/.gitignore => vb-nfc-reader/.idea/.gitignore +0 -3
@@ 1,3 0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
D vb-nfc-reader/.idea/caches/deviceStreaming.xml => vb-nfc-reader/.idea/caches/deviceStreaming.xml +0 -340
@@ 1,340 0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
- <component name="DeviceStreaming">
- <option name="deviceSelectionList">
- <list>
- <PersistentDeviceSelectionData>
- <option name="api" value="27" />
- <option name="brand" value="DOCOMO" />
- <option name="codename" value="F01L" />
- <option name="id" value="F01L" />
- <option name="manufacturer" value="FUJITSU" />
- <option name="name" value="F-01L" />
- <option name="screenDensity" value="360" />
- <option name="screenX" value="720" />
- <option name="screenY" value="1280" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="OPPO" />
- <option name="codename" value="OP573DL1" />
- <option name="id" value="OP573DL1" />
- <option name="manufacturer" value="OPPO" />
- <option name="name" value="CPH2557" />
- <option name="screenDensity" value="480" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2400" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="28" />
- <option name="brand" value="DOCOMO" />
- <option name="codename" value="SH-01L" />
- <option name="id" value="SH-01L" />
- <option name="manufacturer" value="SHARP" />
- <option name="name" value="AQUOS sense2 SH-01L" />
- <option name="screenDensity" value="480" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2160" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="Lenovo" />
- <option name="codename" value="TB370FU" />
- <option name="id" value="TB370FU" />
- <option name="manufacturer" value="Lenovo" />
- <option name="name" value="Tab P12" />
- <option name="screenDensity" value="340" />
- <option name="screenX" value="1840" />
- <option name="screenY" value="2944" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="31" />
- <option name="brand" value="samsung" />
- <option name="codename" value="a51" />
- <option name="id" value="a51" />
- <option name="manufacturer" value="Samsung" />
- <option name="name" value="Galaxy A51" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2400" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="google" />
- <option name="codename" value="akita" />
- <option name="id" value="akita" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 8a" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2400" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="33" />
- <option name="brand" value="samsung" />
- <option name="codename" value="b0q" />
- <option name="id" value="b0q" />
- <option name="manufacturer" value="Samsung" />
- <option name="name" value="Galaxy S22 Ultra" />
- <option name="screenDensity" value="600" />
- <option name="screenX" value="1440" />
- <option name="screenY" value="3088" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="32" />
- <option name="brand" value="google" />
- <option name="codename" value="bluejay" />
- <option name="id" value="bluejay" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 6a" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2400" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="google" />
- <option name="codename" value="caiman" />
- <option name="id" value="caiman" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 9 Pro" />
- <option name="screenDensity" value="360" />
- <option name="screenX" value="960" />
- <option name="screenY" value="2142" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="google" />
- <option name="codename" value="comet" />
- <option name="id" value="comet" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 9 Pro Fold" />
- <option name="screenDensity" value="390" />
- <option name="screenX" value="2076" />
- <option name="screenY" value="2152" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="29" />
- <option name="brand" value="samsung" />
- <option name="codename" value="crownqlteue" />
- <option name="id" value="crownqlteue" />
- <option name="manufacturer" value="Samsung" />
- <option name="name" value="Galaxy Note9" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="2220" />
- <option name="screenY" value="1080" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="samsung" />
- <option name="codename" value="dm3q" />
- <option name="id" value="dm3q" />
- <option name="manufacturer" value="Samsung" />
- <option name="name" value="Galaxy S23 Ultra" />
- <option name="screenDensity" value="600" />
- <option name="screenX" value="1440" />
- <option name="screenY" value="3088" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="samsung" />
- <option name="codename" value="e1q" />
- <option name="id" value="e1q" />
- <option name="manufacturer" value="Samsung" />
- <option name="name" value="Galaxy S24" />
- <option name="screenDensity" value="480" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2340" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="33" />
- <option name="brand" value="google" />
- <option name="codename" value="felix" />
- <option name="id" value="felix" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel Fold" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="2208" />
- <option name="screenY" value="1840" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="google" />
- <option name="codename" value="felix" />
- <option name="id" value="felix" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel Fold" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="2208" />
- <option name="screenY" value="1840" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="33" />
- <option name="brand" value="google" />
- <option name="codename" value="felix_camera" />
- <option name="id" value="felix_camera" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel Fold (Camera-enabled)" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="2208" />
- <option name="screenY" value="1840" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="33" />
- <option name="brand" value="samsung" />
- <option name="codename" value="gts8uwifi" />
- <option name="id" value="gts8uwifi" />
- <option name="manufacturer" value="Samsung" />
- <option name="name" value="Galaxy Tab S8 Ultra" />
- <option name="screenDensity" value="320" />
- <option name="screenX" value="1848" />
- <option name="screenY" value="2960" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="google" />
- <option name="codename" value="husky" />
- <option name="id" value="husky" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 8 Pro" />
- <option name="screenDensity" value="390" />
- <option name="screenX" value="1008" />
- <option name="screenY" value="2244" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="30" />
- <option name="brand" value="motorola" />
- <option name="codename" value="java" />
- <option name="id" value="java" />
- <option name="manufacturer" value="Motorola" />
- <option name="name" value="G20" />
- <option name="screenDensity" value="280" />
- <option name="screenX" value="720" />
- <option name="screenY" value="1600" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="google" />
- <option name="codename" value="komodo" />
- <option name="id" value="komodo" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 9 Pro XL" />
- <option name="screenDensity" value="360" />
- <option name="screenX" value="1008" />
- <option name="screenY" value="2244" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="33" />
- <option name="brand" value="google" />
- <option name="codename" value="lynx" />
- <option name="id" value="lynx" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 7a" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2400" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="31" />
- <option name="brand" value="google" />
- <option name="codename" value="oriole" />
- <option name="id" value="oriole" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 6" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2400" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="33" />
- <option name="brand" value="google" />
- <option name="codename" value="panther" />
- <option name="id" value="panther" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 7" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2400" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="samsung" />
- <option name="codename" value="q5q" />
- <option name="id" value="q5q" />
- <option name="manufacturer" value="Samsung" />
- <option name="name" value="Galaxy Z Fold5" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1812" />
- <option name="screenY" value="2176" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="samsung" />
- <option name="codename" value="q6q" />
- <option name="id" value="q6q" />
- <option name="manufacturer" value="Samsung" />
- <option name="name" value="Galaxy Z Fold6" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1856" />
- <option name="screenY" value="2160" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="30" />
- <option name="brand" value="google" />
- <option name="codename" value="r11" />
- <option name="id" value="r11" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel Watch" />
- <option name="screenDensity" value="320" />
- <option name="screenX" value="384" />
- <option name="screenY" value="384" />
- <option name="type" value="WEAR_OS" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="30" />
- <option name="brand" value="google" />
- <option name="codename" value="redfin" />
- <option name="id" value="redfin" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 5" />
- <option name="screenDensity" value="440" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2340" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="google" />
- <option name="codename" value="shiba" />
- <option name="id" value="shiba" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 8" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2400" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="33" />
- <option name="brand" value="google" />
- <option name="codename" value="tangorpro" />
- <option name="id" value="tangorpro" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel Tablet" />
- <option name="screenDensity" value="320" />
- <option name="screenX" value="1600" />
- <option name="screenY" value="2560" />
- </PersistentDeviceSelectionData>
- <PersistentDeviceSelectionData>
- <option name="api" value="34" />
- <option name="brand" value="google" />
- <option name="codename" value="tokay" />
- <option name="id" value="tokay" />
- <option name="manufacturer" value="Google" />
- <option name="name" value="Pixel 9" />
- <option name="screenDensity" value="420" />
- <option name="screenX" value="1080" />
- <option name="screenY" value="2424" />
- </PersistentDeviceSelectionData>
- </list>
- </option>
- </component>
-</project>>
\ No newline at end of file
D vb-nfc-reader/.idea/gradle.xml => vb-nfc-reader/.idea/gradle.xml +0 -13
@@ 1,13 0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
- <component name="GradleSettings">
- <option name="linkedExternalProjectsSettings">
- <GradleProjectSettings>
- <option name="testRunner" value="CHOOSE_PER_TEST" />
- <option name="externalProjectPath" value="$PROJECT_DIR$" />
- <option name="gradleJvm" value="jbr-21" />
- <option name="resolveExternalAnnotations" value="false" />
- </GradleProjectSettings>
- </option>
- </component>
-</project>>
\ No newline at end of file
D vb-nfc-reader/.idea/misc.xml => vb-nfc-reader/.idea/misc.xml +0 -9
@@ 1,9 0,0 @@
-<project version="4">
- <component name="ExternalStorageConfigurationManager" enabled="true" />
- <component name="ProjectRootManager">
- <output url="file://$PROJECT_DIR$/build/classes" />
- </component>
- <component name="ProjectType">
- <option name="id" value="Android" />
- </component>
-</project>>
\ No newline at end of file
D vb-nfc-reader/.idea/runConfigurations.xml => vb-nfc-reader/.idea/runConfigurations.xml +0 -17
@@ 1,17 0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
- <component name="RunConfigurationProducerService">
- <option name="ignoredProducers">
- <set>
- <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
- <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
- <option value="com.intellij.execution.junit.PatternConfigurationProducer" />
- <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
- <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
- <option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
- <option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
- <option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
- </set>
- </option>
- </component>
-</project>>
\ No newline at end of file
D vb-nfc-reader/build.gradle.kts => vb-nfc-reader/build.gradle.kts +0 -44
@@ 1,44 0,0 @@
-plugins {
- id("com.android.library")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- namespace = "com.github.cfogrady.vbnfc"
- compileSdk = 34
-
- defaultConfig {
- minSdk = 28
-
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles("consumer-rules.pro")
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = "1.8"
- }
-}
-
-dependencies {
- implementation("androidx.core:core-ktx:1.12.0")
- implementation("androidx.appcompat:appcompat:1.6.1")
- implementation("com.google.android.material:material:1.11.0")
- testImplementation("junit:junit:4.13.2")
- androidTestImplementation("androidx.test.ext:junit:1.1.5")
- androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
- testImplementation("io.mockk:mockk-android:1.13.14")
- testImplementation("io.mockk:mockk-agent:1.13.14")
-}>
\ No newline at end of file
D vb-nfc-reader/consumer-rules.pro => vb-nfc-reader/consumer-rules.pro +0 -0
D vb-nfc-reader/proguard-rules.pro => vb-nfc-reader/proguard-rules.pro +0 -21
@@ 1,21 0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile>
\ No newline at end of file
D vb-nfc-reader/src/androidTest/java/com/github/cfogrady/vbnfc/ExampleInstrumentedTest.kt => vb-nfc-reader/src/androidTest/java/com/github/cfogrady/vbnfc/ExampleInstrumentedTest.kt +0 -24
@@ 1,24 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.github.cfogrady.vbnfc.test", appContext.packageName)
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/AndroidManifest.xml => vb-nfc-reader/src/main/AndroidManifest.xml +0 -4
@@ 1,4 0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-</manifest>>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/ByteManipulation.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/ByteManipulation.kt +0 -78
@@ 1,78 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import java.lang.IllegalArgumentException
-import java.nio.ByteOrder
-
-fun ByteArray.getUInt32(index: Int = 0, byteOrder: ByteOrder = ByteOrder.nativeOrder()): UInt {
- if (this.size < index + 4) {
- throw IllegalArgumentException("Must be 4 bytes from index to get a UInt")
- }
- var result: UInt = 0u
- for (i in 0 until 4) {
- result = if (byteOrder == ByteOrder.BIG_ENDIAN) {
- result or ((this[index+i].toUInt() and 0xFFu) shl 8*(3 - i))
- } else {
- result or ((this[index+i].toUInt() and 0xFFu) shl 8*(i))
- }
- }
- return result
-}
-
-fun UInt.toByteArray(byteOrder: ByteOrder = ByteOrder.nativeOrder()): ByteArray {
- val byteArray = byteArrayOf(0, 0, 0, 0)
- for(i in 0 until 4) {
- if(byteOrder == ByteOrder.LITTLE_ENDIAN) {
- byteArray[i] = ((this shr 8*i) and 255u).toByte()
- } else {
- byteArray[3-i] = ((this shr 8*i) and 255u).toByte()
- }
- }
- return byteArray
-}
-
-fun ByteArray.getUInt16(index: Int = 0, byteOrder: ByteOrder = ByteOrder.nativeOrder()): UShort {
- if (this.size < index + 2) {
- throw IllegalArgumentException("Must be 2 bytes from index to get a UInt")
- }
- var result: UInt = 0u
- for (i in 0 until 2) {
- result = if (byteOrder == ByteOrder.BIG_ENDIAN) {
- result or ((this[index+i].toUInt() and 0xFFu) shl 8*(1 - i))
- } else {
- result or ((this[index+i].toUInt() and 0xFFu) shl 8*(i))
- }
- }
- return result.toUShort()
-}
-
-fun UShort.toByteArray(byteOrder: ByteOrder = ByteOrder.nativeOrder()): ByteArray {
- val byteArray = byteArrayOf(0, 0)
- val asUInt = this.toUInt()
- for(i in 0 until 2) {
- if(byteOrder == ByteOrder.LITTLE_ENDIAN) {
- byteArray[i] = ((asUInt shr 8*i) and 255u).toByte()
- } else {
- byteArray[1-i] = ((asUInt shr 8*i) and 255u).toByte()
- }
- }
- return byteArray
-}
-
-fun UShort.toByteArray(bytes: ByteArray, dstIndex: Int, byteOrder: ByteOrder = ByteOrder.nativeOrder()) {
- val asUInt = this.toUInt()
- for(i in 0 until 2) {
- if(byteOrder == ByteOrder.LITTLE_ENDIAN) {
- bytes[i+dstIndex] = ((asUInt shr 8*i) and 255u).toByte()
- } else {
- bytes[(1-i) + dstIndex] = ((asUInt shr 8*i) and 255u).toByte()
- }
- }
-}
-
-fun ByteArray.copyIntoUShortArray(offset: Int, length: Int): Array<UShort> {
- val result = Array<UShort>(length) { 0u }
- for (i in 0..<length) {
- result[i] = this.getUInt16(offset + i * 2, ByteOrder.BIG_ENDIAN)
- }
- return result
-}
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/ChecksumCalculator.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/ChecksumCalculator.kt +0 -41
@@ 1,41 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import java.lang.IllegalStateException
-
-class ChecksumCalculator {
- companion object {
- private val PagesWithChecksum = hashSetOf(8, 16, 24, 32, 40, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 104, 192, 200, 208, 216)
- }
-
- @OptIn(ExperimentalStdlibApi::class)
- fun checkChecksums(data: ByteArray) {
- operateOnChecksums(data) { checksumByte, checksumIdx ->
- if (checksumByte != data[checksumIdx]) {
- throw IllegalStateException("Checksum ${checksumByte.toHexString()} doesn't match expected ${data[checksumIdx].toHexString()}")
- }
- }
- }
-
- @OptIn(ExperimentalStdlibApi::class)
- fun recalculateChecksums(data: ByteArray) {
- operateOnChecksums(data) { checksumByte, checksumIdx ->
- data[checksumIdx] = checksumByte
- }
- }
-
- private fun operateOnChecksums(data: ByteArray, operator: (Byte, Int)->Unit) {
- // loop through all data
- for(i in data.indices step 16) {
- val page = i/4 + 8 // first 8 pages are header data and not part of the character data
- if (PagesWithChecksum.contains(page)) {
- var sum = 0
- val checksumIndex = i + 15
- for(j in i..<checksumIndex) {
- sum += data[j]
- }
- val checksumByte = (sum and 0xff).toByte()
- operator(checksumByte, checksumIndex)
- }
- }
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/CryptographicTransformer.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/CryptographicTransformer.kt +0 -110
@@ 1,110 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import java.nio.charset.StandardCharsets
-import java.util.Base64
-import javax.crypto.Cipher
-import javax.crypto.Mac
-import javax.crypto.spec.IvParameterSpec
-import javax.crypto.spec.SecretKeySpec
-import kotlin.experimental.xor
-
-class CryptographicTransformer(private val readableHmacKey1: String, private val readableHmacKey2: String, private val aesKey: String, private val substitutionCipher: IntArray) {
-
- companion object {
- const val HMAC256 = "HmacSHA256"
- }
-
- // Creates a 4 byte password by hashing the current data using HMAC256 with the first key. Then
- // applying a 4-bit substitution cypher on the result, and hashing again with the second key.
- // The password are the 4 bytes starting at index 28 of that result.
- fun createNfcPassword(inputData: ByteArray): ByteArray {
- val hmacKey1 = decryptHmacKey(readableHmacKey1)
- val hmacKey2 = decryptHmacKey(readableHmacKey2)
- val hashedInput = generateHMacSHA256Hash(hmacKey1, inputData)
- val substitutedBytes = apply4BitSubstitutionCipher(hashedInput)
- val secondHash = generateHMacSHA256Hash(hmacKey2, substitutedBytes)
- return secondHash.sliceArray(28..<32)
- }
-
- fun decryptData(data: ByteArray, tagId: ByteArray): ByteArray {
- val hmacKey1 = decryptHmacKey(readableHmacKey1)
- val hmacKey2 = decryptHmacKey(readableHmacKey2)
- return cryptoTransformation(Cipher.DECRYPT_MODE, data, tagId, hmacKey1, hmacKey2)
- }
-
- fun encryptData(data: ByteArray, tagId: ByteArray): ByteArray {
- val hmacKey1 = decryptHmacKey(readableHmacKey1)
- val hmacKey2 = decryptHmacKey(readableHmacKey2)
- return cryptoTransformation(Cipher.ENCRYPT_MODE, data, tagId, hmacKey1, hmacKey2)
- }
-
- private fun decryptHmacKey(str: String): String {
- val decoded = Base64.getDecoder().decode(str)
- val decrypt = decryptAesCbcPkcs5Padding(aesKey, decoded)
- return String(decrypt, StandardCharsets.UTF_8)
- }
-
- private fun decryptAesCbcPkcs5Padding(key: String, data: ByteArray): ByteArray {
- val keyBytes = key.toByteArray(StandardCharsets.UTF_8)
- val rightSizedKey = keyBytes.copyOf(32)
- val ivBytes = keyBytes.copyOfRange(key.length - 16, key.length)
- val secretKeySpec = SecretKeySpec(rightSizedKey, "AES")
- val ivParameterSpec = IvParameterSpec(ivBytes)
- val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
- cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
- return cipher.doFinal(data)
- }
-
- private fun generateHMacSHA256Hash(hmacKey: String, data: ByteArray): ByteArray {
- val hmacKeyBytes = hmacKey.toByteArray(StandardCharsets.US_ASCII)
- val secretKeySpec = SecretKeySpec(hmacKeyBytes, HMAC256)
- val mac = Mac.getInstance(HMAC256)
- mac.init(secretKeySpec)
- return mac.doFinal(data)
- }
-
- // This is a 4 bit substitution cipher, where each 4 bits act as an index to another 4 bits
- private fun apply4BitSubstitutionCipher(data: ByteArray): ByteArray {
- val result = ByteArray(data.size)
- for (idx in data.indices) {
- val byte: Int = data[idx].toInt()
- var newByte = 0
- for (fourBitShifts in 0..<2) { // perform one OR without shift, and one OR shifted 4 bits
- val shift = fourBitShifts * 4
- val permutationIndex = (byte shr shift) and 0xF
- newByte = newByte or (substitutionCipher[permutationIndex] shl shift)
- }
- result[idx] = newByte.toByte()
- }
- return result
- }
-
- // Hashes the tagId once and applies substitution cipher. Then hashes again.
- // Splits hash and original tagId into key and initialization vector
- private fun cryptoTransformation(cipherMode: Int, data: ByteArray, tagId: ByteArray, hmacKey1: String, hmacKey2: String): ByteArray {
- var hashedTagId = apply4BitSubstitutionCipher(generateHMacSHA256Hash(hmacKey1, tagId))
- hashedTagId = generateHMacSHA256Hash(hmacKey2, hashedTagId) // second hash
-
- // generate actual key and initializing vector from tagIdGeneratedKey
- val iv1 = ByteArray(15)
- hashedTagId.copyInto(iv1, 0, 24, 32)
- tagId.copyInto(iv1, 8, 0, 7)
- val iv2 = hashedTagId.copyOf(15)
- val ivParameterSpec = IvParameterSpec(xorBytes(iv1, iv2, 16))
- val secretKeySpec = SecretKeySpec(hashedTagId.copyOf(16), "AES")
- val cipher = Cipher.getInstance("AES/CTR/NoPadding")
- cipher.init(cipherMode, secretKeySpec, ivParameterSpec)
- return cipher.doFinal(data)
- }
-
-
-
- private fun xorBytes(data1: ByteArray, data2: ByteArray, resultSize: Int): ByteArray {
- val results = ByteArray(resultSize)
- results.fill(0)
- for (i in data1.indices) {
- results[i] = data1[i] xor data2[i]
- }
- return results
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/NfcDataTranslator.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/NfcDataTranslator.kt +0 -30
@@ 1,30 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import com.github.cfogrady.vbnfc.data.NfcCharacter
-import com.github.cfogrady.vbnfc.data.NfcHeader
-
-interface NfcDataTranslator {
-
- // setCharacterInByteArray takes the NfcCharacter and modifies the byte array with character
- // data. At the time of writing this is used to write a parsed character into fresh unparsed
- // device data when sending a character back to the device.
- fun setCharacterInByteArray(character: NfcCharacter, bytes: ByteArray) {
- }
-
- // finalizeByteArrayFormat finalizes the byte array for NFC format by setting all the
- // checksums, and duplicating the duplicate memory pages.
- fun finalizeByteArrayFormat(bytes: ByteArray)
-
- // getOperationCommandBytes gets an operation command corresponding to the existing header and
- // the input operation
- fun getOperationCommandBytes(header: NfcHeader, operation: Byte): ByteArray
-
- // parseNfcCharacter parses the nfc data byte array into an instance of a NfcCharacter object
- fun parseNfcCharacter(bytes: ByteArray): NfcCharacter
-
- // parseHeader parses the nfc header byte array into an instance of NfcHeader
- fun parseHeader(headerBytes: ByteArray): NfcHeader
-
- val cryptographicTransformer: CryptographicTransformer
-
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/NfcDataTranslatorFactory.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/NfcDataTranslatorFactory.kt +0 -22
@@ 1,22 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import com.github.cfogrady.vbnfc.be.BENfcDataTranslator
-import com.github.cfogrady.vbnfc.data.DeviceType
-
-class NfcDataTranslatorFactory(
- private val translators: MutableMap<UShort,NfcDataTranslator> = HashMap()
-) {
-
-
- fun getNfcDataTranslator(deviceTypeId: UShort): NfcDataTranslator {
- val dataTranslator = translators[deviceTypeId]
- if(dataTranslator != null) {
- return dataTranslator
- }
- throw UnsupportedOperationException("Device type ${deviceTypeId} is not yet supported")
- }
-
- fun addNfcDataTranslator(nfcDataTranslator: NfcDataTranslator, deviceTypeId: UShort) {
- translators[deviceTypeId] = nfcDataTranslator
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/TagCommunicator.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/TagCommunicator.kt +0 -193
@@ 1,193 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import android.nfc.tech.NfcA
-import android.util.Log
-import com.github.cfogrady.vbnfc.be.BENfcDataTranslator
-import com.github.cfogrady.vbnfc.data.DeviceType
-import com.github.cfogrady.vbnfc.data.NfcCharacter
-import com.github.cfogrady.vbnfc.data.NfcHeader
-import java.nio.ByteOrder
-
-class TagCommunicator(
- private val nfcData: NfcA,
- private val checksumCalculator: ChecksumCalculator,
- private val nfcDataTranslatorFactory: NfcDataTranslatorFactory,
- ) {
-
- companion object {
- const val TAG = "VBNfcHandler"
- const val HEADER_PAGE: Byte = 0x04
- const val NFC_PASSWORD_COMMAND: Byte = 0x1b
- const val NFC_READ_COMMAND: Byte = 0x30
- const val NFC_WRITE_COMMAND: Byte = 0xA2.toByte()
-
- const val STATUS_IDLE: Byte = 0
- const val STATUS_READY: Byte = 1
-
- const val OPERATION_IDLE: Byte = 0
- const val OPERATION_READY: Byte = 1
- const val OPERATION_TRANSFERRED_TO_APP: Byte = 2
- const val OPERATION_CHECK_DIM: Byte = 3
- const val OPERATION_TRANSFERED_TO_DEVICE: Byte = 4
- const val START_DATA_PAGE = 8
- const val LAST_DATA_PAGE = 220 // technically 223, but we read 4 pages at a time.
-
- fun getInstance(nfcData: NfcA, deviceTypeIdSecrets: Map<UShort, CryptographicTransformer>): TagCommunicator {
- val checksumCalculator = ChecksumCalculator()
- val deviceToTranslator = HashMap<UShort, NfcDataTranslator>()
- for (keyValue in deviceTypeIdSecrets) {
- when(keyValue.key) {
- DeviceType.VitalBraceletBEDeviceType -> {
- deviceToTranslator[keyValue.key] = BENfcDataTranslator(keyValue.value, checksumCalculator)
- }
- else -> {
- throw IllegalArgumentException("DeviceId ${keyValue.key} Provided Without Known Parser")
- }
- }
- }
- return TagCommunicator(nfcData, checksumCalculator, NfcDataTranslatorFactory(deviceToTranslator))
- }
-
- }
-
- data class DeviceTranslatorAndHeader(val nfcHeader: NfcHeader, val translator: NfcDataTranslator)
-
- @OptIn(ExperimentalStdlibApi::class)
- fun receiveCharacter(): NfcCharacter {
- val translatorAndHeader = fetchDeviceTranslatorAndHeader()
- val header = translatorAndHeader.nfcHeader
- val translator = translatorAndHeader.translator
- Log.i(TAG, "Writing to make ready for operation")
- nfcData.transceive(translator.getOperationCommandBytes(header, OPERATION_READY))
- Log.i(TAG, "Authenticating")
-
- passwordAuth(translator.cryptographicTransformer)
- Log.i(TAG, "Reading Character")
- val encryptedCharacterData = readNfcData()
- Log.i(TAG, "Raw NFC Data Received: ${encryptedCharacterData.toHexString()}")
- val decryptedCharacterData = translator.cryptographicTransformer.decryptData(encryptedCharacterData, nfcData.tag.id)
- checksumCalculator.checkChecksums(decryptedCharacterData)
- val nfcCharacter = translator.parseNfcCharacter(decryptedCharacterData)
- Log.i(TAG, "Known Character Stats: $nfcCharacter")
- // Not ready to lose any of my mons in this...
- //Log.i(TAG, "Signaling operation complete")
- //nfcData.transceive(translator.getOperationCommandBytes(header, OPERATION_TRANSFERRED_TO_APP))
- return nfcCharacter
- }
-
- @OptIn(ExperimentalStdlibApi::class)
- private fun fetchDeviceTranslatorAndHeader(): DeviceTranslatorAndHeader {
- val readData = nfcData.transceive(byteArrayOf(NFC_READ_COMMAND, HEADER_PAGE))
- Log.i("TagCommunicator", "First 4 Pages: ${readData.toHexString()}")
- val deviceTypeId = readData.getUInt16(4, ByteOrder.BIG_ENDIAN)
- val translator = nfcDataTranslatorFactory.getNfcDataTranslator(deviceTypeId)
- val header = translator.parseHeader(readData)
- return DeviceTranslatorAndHeader(header, translator)
- }
-
- private fun readNfcData(): ByteArray {
- val result = ByteArray(((LAST_DATA_PAGE +4)- START_DATA_PAGE) * 4)
- for (page in START_DATA_PAGE..LAST_DATA_PAGE step 4) {
- val pages = nfcData.transceive(byteArrayOf(NFC_READ_COMMAND, page.toByte()))
- if (pages.size < 16) {
- throw Exception("Failed to read page: $page")
- }
- System.arraycopy(pages, 0, result, (page - START_DATA_PAGE)*4, pages.size)
- }
- return result
- }
-
- fun prepareDIMForCharacter(dimId: UShort) {
- val translatorAndHeader = fetchDeviceTranslatorAndHeader()
- val header = translatorAndHeader.nfcHeader
- val translator = translatorAndHeader.translator
- // set app nonce to device ensure when we send back the character that we are preparing
- // the same device we send to
- nfcData.transceive(translator.getOperationCommandBytes(header, OPERATION_READY))
- // app authenticates, and reads everything, and checks the version
- // The version check is only for the BE when transfering from DIM=0 (pulsemon).
- // This was from the bug when the BE first came out.
- // Check (page 103 [0:1] != 1, 0)
- header.setDimId(dimId)
- nfcData.transceive(translator.getOperationCommandBytes(header, OPERATION_CHECK_DIM))
- }
-
- private fun defaultNfcDataGenerator(translator: NfcDataTranslator, character: NfcCharacter): ByteArray {
- val currentNfcData = readNfcData()
- val newNfcData = translator.cryptographicTransformer.decryptData(currentNfcData, nfcData.tag.id)
- translator.setCharacterInByteArray(character, newNfcData)
- checksumCalculator.recalculateChecksums(newNfcData)
- translator.finalizeByteArrayFormat(newNfcData)
- return newNfcData
- }
-
- // sendCharacter sends a character to the device using the nfcDataGenerator function. The
- // default nfcDataGenerator reads the current data of the device and applies the new character
- // data to the read data and prepares that to be sent back to the device. The nfcDataGenerator
- // is a functor which takes in the NfcDataTranslator for the device and the NfcCharacter
- // provided to the sendCharacter method and returns the decrypted byte array data to be sent
- // back to the device.
- @OptIn(ExperimentalStdlibApi::class)
- fun sendCharacter(character: NfcCharacter, nfcDataGenerator: (NfcDataTranslator, NfcCharacter) -> ByteArray = this::defaultNfcDataGenerator) {
- Log.i(TAG, "Sending Character: $character")
- val deviceTranslatorAndHeader = fetchDeviceTranslatorAndHeader()
- val translator = deviceTranslatorAndHeader.translator
- val header = deviceTranslatorAndHeader.nfcHeader
-
- // check the nonce
- // if it's not expected, then the bracelet isn't ready
-
- // Check the product and device ids that they match the target.
- // This check relies on the app categories of BE, Vital Hero, and Vital Series.
-
- // ensure the dim id matches the expected
- if (character.dimId != header.getDimId()) {
- throw IllegalArgumentException("Device is ready for DIM ${header.getDimId()}, but attempted to send ${character.dimId}")
- }
-
- // update the memory data
- nfcData.transceive(translator.getOperationCommandBytes(header, OPERATION_READY))
- passwordAuth(translator.cryptographicTransformer)
-
- var newNfcData = nfcDataGenerator(translator, character)
- newNfcData = translator.cryptographicTransformer.encryptData(newNfcData, nfcData.tag.id)
- Log.i(TAG, "Sending Character: ${newNfcData.toHexString()}")
-
-
- // write nfc data
- val pagedData = ConvertToPages(newNfcData)
- for(pageToWriteIdx in 8..<pagedData.size) {
- val pageToWrite = pagedData[pageToWriteIdx]
- nfcData.transceive(byteArrayOf(NFC_WRITE_COMMAND, pageToWriteIdx.toByte(), pageToWrite[0], pageToWrite[1], pageToWrite[2], pageToWrite[3]))
- }
-
-
- nfcData.transceive(translator.getOperationCommandBytes(header, OPERATION_TRANSFERED_TO_DEVICE))
-
- }
-
- @OptIn(ExperimentalStdlibApi::class)
- fun passwordAuth(cryptographicTransformer: CryptographicTransformer) {
- val tagId = nfcData.tag.id
- Log.i(TAG, "TagId: ${tagId.toHexString()}")
- val password = cryptographicTransformer.createNfcPassword(tagId)
- try {
- val result = nfcData.transceive(byteArrayOf(NFC_PASSWORD_COMMAND, password[0], password[1], password[2], password[3]))
- Log.i(TAG, "PasswordAuth Result: ${result.toHexString()}")
- if (result.size == 1) {
- throw AuthenticationException("Authentication failed. Result: ${result.toHexString()}")
- }
- } catch (e: Exception) {
- Log.e(TAG, "Exception: ${e.message}")
- }
- }
-
- // addDataTranslator adds a new data translator to be used with the specified deviceTypeId.
- // This can be used to keep the same general communication protocol, but allows for a different
- // parsing of the data.
- fun addDataTranslator(nfcDataTranslator: NfcDataTranslator, deviceTypeId: UShort) {
- nfcDataTranslatorFactory.addNfcDataTranslator(nfcDataTranslator, deviceTypeId)
- }
-
- class AuthenticationException(message: String): Exception(message)
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/Utils.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/Utils.kt +0 -20
@@ 1,20 0,0 @@
-package com.github.cfogrady.vbnfc
-
-// ConverToPages converts the byte array into the paged structure used in NFC communication
-// If data for the header isn't included, the first 8 pages will be 0 filled.
-fun ConvertToPages(data: ByteArray, header: ByteArray? = null) : List<ByteArray> {
- val pages = ArrayList<ByteArray>()
- // setup blank header pages
- for (i in 0..7) {
- if (header != null) {
- val index = i*4
- pages.add(header.sliceArray(index..<index+4))
- } else {
- pages.add(byteArrayOf(0, 0, 0, 0))
- }
- }
- for(i in data.indices step 4) {
- pages.add(data.sliceArray(i..<i+4))
- }
- return pages
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcCharacter.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcCharacter.kt +0 -183
@@ 1,183 0,0 @@
-package com.github.cfogrady.vbnfc.be
-
-import com.github.cfogrady.vbnfc.data.NfcCharacter
-import java.util.Arrays
-import java.util.Objects
-
-class BENfcCharacter(
- dimId: UShort,
- charIndex: UShort,
- stage: Byte,
- attribute: Attribute,
- ageInDays: Byte,
- nextAdventureMissionStage: Byte,
- mood: Byte,
- vitalPoints: UShort,
- transformationCountdownInMinutes: UShort,
- injuryStatus: InjuryStatus,
- trainingPp: UShort,
- currentPhaseBattlesWon: UShort,
- currentPhaseBattlesLost: UShort,
- totalBattlesWon: UShort,
- totalBattlesLost: UShort,
- activityLevel: Byte,
- heartRateCurrent: UByte,
- transformationHistory: Array<Transformation>,
- var trainingHp: UShort,
- var trainingAp: UShort,
- var trainingBp: UShort,
- var remainingTrainingTimeInMinutes: UShort,
- var itemEffectMentalStateValue: Byte,
- var itemEffectMentalStateMinutesRemaining: Byte,
- var itemEffectActivityLevelValue: Byte,
- var itemEffectActivityLevelMinutesRemaining: Byte,
- var itemEffectVitalPointsChangeValue: Byte,
- var itemEffectVitalPointsChangeMinutesRemaining: Byte,
- var abilityRarity: AbilityRarity,
- var abilityType: UShort,
- var abilityBranch: UShort,
- var abilityReset: Byte,
- var rank: Byte,
- var itemType: Byte,
- var itemMultiplier: Byte,
- var itemRemainingTime: Byte,
- internal val otp0: ByteArray, // OTP matches the character to the dim
- internal val otp1: ByteArray, // OTP matches the character to the dim
- var characterCreationFirmwareVersion: FirmwareVersion,
- var appReserved1: ByteArray, // this is a 12 byte array reserved for new app features, a custom app should be able to safely use this for custom features
- var appReserved2: Array<UShort>, // this is a 3 element array reserved for new app features, a custom app should be able to safely use this for custom features
-) :
- NfcCharacter(
- dimId = dimId,
- charIndex = charIndex,
- stage = stage,
- attribute = attribute,
- ageInDays = ageInDays,
- nextAdventureMissionStage = nextAdventureMissionStage,
- mood = mood,
- vitalPoints = vitalPoints,
- transformationCountdown = transformationCountdownInMinutes,
- injuryStatus = injuryStatus,
- trophies = trainingPp,
- currentPhaseBattlesWon = currentPhaseBattlesWon,
- currentPhaseBattlesLost = currentPhaseBattlesLost,
- totalBattlesWon = totalBattlesWon,
- totalBattlesLost = totalBattlesLost,
- activityLevel = activityLevel,
- heartRateCurrent = heartRateCurrent,
- transformationHistory = transformationHistory,
- )
-{
- fun getTrainingPp(): UShort {
- return trophies
- }
-
- fun setTrainingPp(trainingPp: UShort) {
- trophies = trainingPp
- }
-
- fun getWinPercentage(): Byte {
- val totalBatles = currentPhaseBattlesWon + currentPhaseBattlesLost
- if (totalBatles == 0u) {
- return 0
- }
- return ((100u * currentPhaseBattlesWon) / totalBatles).toByte()
- }
-
-
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as BENfcCharacter
- if(!super.equals(other)) return false
-
- if (trainingHp != other.trainingHp) return false
- if (trainingAp != other.trainingAp) return false
- if (trainingBp != other.trainingBp) return false
- if (remainingTrainingTimeInMinutes != other.remainingTrainingTimeInMinutes) return false
- if (itemEffectMentalStateValue != other.itemEffectMentalStateValue) return false
- if (itemEffectMentalStateMinutesRemaining != other.itemEffectMentalStateMinutesRemaining) return false
- if (itemEffectActivityLevelValue != other.itemEffectActivityLevelValue) return false
- if (itemEffectActivityLevelMinutesRemaining != other.itemEffectActivityLevelMinutesRemaining) return false
- if (itemEffectVitalPointsChangeValue != other.itemEffectVitalPointsChangeValue) return false
- if (itemEffectVitalPointsChangeMinutesRemaining != other.itemEffectVitalPointsChangeMinutesRemaining) return false
- if (abilityRarity != other.abilityRarity) return false
- if (abilityType != other.abilityType) return false
- if (abilityBranch != other.abilityBranch) return false
- if (abilityReset != other.abilityReset) return false
- if (rank != other.rank) return false
- if (itemType != other.itemType) return false
- if (itemMultiplier != other.itemMultiplier) return false
- if (itemRemainingTime != other.itemRemainingTime) return false
- if (!otp0.contentEquals(other.otp0)) return false
- if (!otp1.contentEquals(other.otp1)) return false
- if (characterCreationFirmwareVersion != other.characterCreationFirmwareVersion) return false
- if (!appReserved1.contentEquals(other.appReserved1)) return false
- if (!appReserved2.contentEquals(other.appReserved2)) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return Objects.hash(
- super.hashCode(),
- trainingHp,
- trainingAp,
- trainingBp,
- remainingTrainingTimeInMinutes,
- itemEffectMentalStateValue,
- itemEffectMentalStateMinutesRemaining,
- itemEffectActivityLevelValue,
- itemEffectActivityLevelMinutesRemaining,
- itemEffectVitalPointsChangeValue,
- itemEffectVitalPointsChangeMinutesRemaining,
- abilityRarity,
- abilityType,
- abilityBranch,
- abilityReset,
- rank,
- itemType,
- itemMultiplier,
- itemRemainingTime,
- otp0.contentHashCode(),
- otp1.contentHashCode(),
- characterCreationFirmwareVersion,
- appReserved1.contentHashCode(),
- appReserved2.contentHashCode())
- }
-
- @OptIn(ExperimentalStdlibApi::class)
- override fun toString(): String {
- return """
-${super.toString()}
-BENfcCharacter(
- trainingHp=$trainingHp,
- trainingAp=$trainingAp,
- trainingBp=$trainingBp,
- remainingTrainingTimeInMinutes=$remainingTrainingTimeInMinutes,
- itemEffectMentalStateValue=$itemEffectMentalStateValue,
- itemEffectMentalStateMinutesRemaining=$itemEffectMentalStateMinutesRemaining,
- itemEffectActivityLevelValue=$itemEffectActivityLevelValue,
- itemEffectActivityLevelMinutesRemaining=$itemEffectActivityLevelMinutesRemaining,
- itemEffectVitalPointsChangeValue=$itemEffectVitalPointsChangeValue,
- itemEffectVitalPointsChangeMinutesRemaining=$itemEffectVitalPointsChangeMinutesRemaining,
- abilityRarity=$abilityRarity,
- abilityType=$abilityType,
- abilityBranch=$abilityBranch,
- abilityReset=$abilityReset,
- rank=$rank,
- itemType=$itemType,
- itemMultiplier=$itemMultiplier,
- itemRemainingTime=$itemRemainingTime,
- otp0=${otp0.toHexString()},
- otp1=${otp1.toHexString()},
- characterCreationFirmwareVersion=$characterCreationFirmwareVersion,
- appReserved1=${appReserved1.contentToString()},
- appReserved2=${appReserved2.contentToString()}
-)"""
- }
-
-
-}
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDataFactory.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDataFactory.kt +0 -68
@@ 1,68 0,0 @@
-package com.github.cfogrady.vbnfc.be
-
-import com.github.cfogrady.vbnfc.ChecksumCalculator
-
-// Obsolete... being held onto for reference to device side data.
-class BENfcDataFactory(checksumCalculator: ChecksumCalculator = ChecksumCalculator()) {
-
-
-
- fun buildBENfcDevice(bytes: ByteArray): BENfcDevice {
- return BENfcDevice(
- gender = BENfcDevice.Gender.entries[bytes[12].toInt()],
- registedDims = bytes.sliceArray(32..<32+15),
- currDays = bytes[78]
- )
-
-
-
-// reserved1 = readByte(107)
-// saveFirmwareVersion = readUShort(108)
-// advMissionStage = readByte(128)
-//
-//
-//
-//
-// reserved2 = readByte(140)
-//
-// reserved3 = readUShort(162)
-// year = readByte(164)
-// month = readByte(165)
-// day = readByte(166)
-// vitalPointsHistory0 = readUShort(176)
-// vitalPointsHistory1 = readUShort(178)
-// vitalPointsHistory2 = readUShort(180)
-// vitalPointsHistory3 = readUShort(182)
-// vitalPointsHistory4 = readUShort(184)
-// vitalPointsHistory5 = readUShort(186)
-// year0PreviousVitalPoints = readByte(188)
-// month0PreviousVitalPoints = readByte(189)
-// day0PreviousVitalPoints = readByte(190)
-// year1PreviousVitalPoints = readByte(192)
-// month1PreviousVitalPoints = readByte(193)
-// day1PreviousVitalPoints = readByte(194)
-// year2PreviousVitalPoints = readByte(195)
-// month2PreviousVitalPoints = readByte(196)
-// day2PreviousVitalPoints = readByte(197)
-// year3PreviousVitalPoints = readByte(198)
-// month3PreviousVitalPoints = readByte(199)
-// day3PreviousVitalPoints = readByte(200)
-// year4PreviousVitalPoints = readByte(201)
-// month4PreviousVitalPoints = readByte(202)
-// day4PreviousVitalPoints = readByte(203)
-// year5PreviousVitalPoints = readByte(204)
-// month5PreviousVitalPoints = readByte(205)
-// day5PreviousVitalPoints = readByte(206)
-//
-// reserved4 = readUShort(262)
-// reserved5 = readByte(264)
-// questionMark = readByte(265)
-// reserved6 = readByte(289)
-// reserved7 = readByte(291)
-//
-// reserved8 = readUShort(297)
-// reserved9 = readUShortArray(376, 2)
-//
- }
-
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDataTranslator.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDataTranslator.kt +0 -271
@@ 1,271 0,0 @@
-package com.github.cfogrady.vbnfc.be
-
-import android.util.Log
-import com.github.cfogrady.vbnfc.ChecksumCalculator
-import com.github.cfogrady.vbnfc.CryptographicTransformer
-import com.github.cfogrady.vbnfc.NfcDataTranslator
-import com.github.cfogrady.vbnfc.TagCommunicator
-import com.github.cfogrady.vbnfc.copyIntoUShortArray
-import com.github.cfogrady.vbnfc.data.DeviceSubType
-import com.github.cfogrady.vbnfc.data.DeviceType
-import com.github.cfogrady.vbnfc.data.NfcCharacter
-import com.github.cfogrady.vbnfc.data.NfcHeader
-import com.github.cfogrady.vbnfc.getUInt16
-import com.github.cfogrady.vbnfc.toByteArray
-import java.nio.ByteOrder
-
-class BENfcDataTranslator(
- override val cryptographicTransformer: CryptographicTransformer,
- private val checksumCalculator: ChecksumCalculator = ChecksumCalculator()
-): NfcDataTranslator {
-
- companion object {
-
- const val OPERATION_PAGE: Byte = 0x6
-
- // CHARACTER
- const val APP_RESERVED_START = 0
- const val APP_RESERVED_SIZE = 12
- const val INJURY_STATUS_IDX = 64
- const val APP_RESERVED_2_START = 66
- const val APP_RESERVED_2_SIZE = 3 //3 ushorts
- const val CHARACTER_INDEX_IDX = 72
- const val DIM_ID_IDX = 74
- const val PHASE_IDX = 76
- const val ATTRIBUTE_IDX = 77
- const val AGE_IN_DAYS_IDX = 78 // always 0 on BE :(
- const val TRAINING_PP_IDX = 96
- const val CURRENT_BATTLES_WON_IDX = 98
- const val CURRENT_BATTLES_LOST_IDX = 100
- const val TOTAL_BATTLES_WON_IDX = 102
- const val TOTAL_BATTLES_LOST_IDX = 104
- const val WIN_PCT_IDX = 106 // unused
- const val CHARACTER_CREATION_FIRMWARE_VERSION_IDX = 108
- const val NEXT_ADVENTURE_MISSION_STAGE_IDX = 128
- const val MOOD_IDX = 129
- const val ACTIVITY_LEVEL_IDX = 130
- const val HEART_RATE_CURRENT_IDX = 131
- const val VITAL_POINTS_IDX = 132
- const val ITEM_EFFECT_MENTAL_STATE_VALUE_IDX = 134
- const val ITEM_EFFECT_MENTAL_STATE_MINUTES_REMAINING_IDX = 135
- const val ITEM_EFFECT_ACTIVITY_LEVEL_VALUE_IDX = 136
- const val ITEM_EFFECT_ACTIVITY_LEVEL_MINUTES_REMAINING_IDX = 137
- const val ITEM_EFFECT_VITAL_POINTS_CHANGE_VALUE_IDX = 138
- const val ITEM_EFFECT_VITAL_POINTS_CHANGE_MINUTES_REMAINING_IDX = 139
- // 140 reserved
- const val TRANSFORMATION_COUNT_DOWN_IDX = 141
- const val TRANSFORMATION_HISTORY_START = 208
- const val TRAINING_HP_IDX = 256
- const val TRAINING_AP_IDX = 258
- const val TRAINING_BP_IDX = 260
- const val TRAINING_TIME_IDX = 266
- const val RANK_IDX = 288
- const val ABILITY_RARITY_IDX = 290
- const val ABILITY_TYPE_IDX = 292
- const val ABILITY_BRANCH_IDX = 294
- const val ABILITY_RESET_IDX = 296
- const val ITEM_TYPE_IDX = 299
- const val ITEM_MULTIPLIER_IDX = 300
- const val ITEM_REMAINING_TIME_IDX = 301
- const val OTP_START_IDX = 352
- const val OTP_END_IDX = 359
- const val OTP2_START_IDX = 368
- const val OTP2_END_IDX = 375
-
- // DEVICE
- const val VITAL_POINTS_CURRENT_IDX = 160
- const val FIMRWARE_VERSION_IDX = 380
-
-
- }
-
- // setCharacterInByteArray takes the BENfcCharacter and modifies the byte array with character
- // data. At the time of writing this is used to write a parsed character into fresh unparsed
- // device data when sending a character back to the device.
- override fun setCharacterInByteArray(
- character: NfcCharacter,
- bytes: ByteArray
- ) {
- val beCharacter = character as BENfcCharacter
- beCharacter.appReserved1.copyInto(bytes,
- APP_RESERVED_START, 0,
- APP_RESERVED_SIZE
- )
- beCharacter.injuryStatus.ordinal.toUShort().toByteArray(bytes,
- INJURY_STATUS_IDX, ByteOrder.BIG_ENDIAN)
- for(i in 0..<APP_RESERVED_2_SIZE) {
- val index = APP_RESERVED_2_START + 2*i
- beCharacter.appReserved2[i].toByteArray(bytes, index, ByteOrder.BIG_ENDIAN)
- }
- beCharacter.charIndex.toByteArray(bytes, CHARACTER_INDEX_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.dimId.toByteArray(bytes, DIM_ID_IDX, ByteOrder.BIG_ENDIAN)
- bytes[PHASE_IDX] = beCharacter.stage
- bytes[ATTRIBUTE_IDX] = beCharacter.attribute.ordinal.toByte()
- bytes[AGE_IN_DAYS_IDX] = beCharacter.ageInDays
- beCharacter.getTrainingPp().toByteArray(bytes, TRAINING_PP_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.currentPhaseBattlesWon.toByteArray(bytes,
- CURRENT_BATTLES_WON_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.currentPhaseBattlesLost.toByteArray(bytes,
- CURRENT_BATTLES_LOST_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.totalBattlesWon.toByteArray(bytes,
- TOTAL_BATTLES_WON_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.totalBattlesLost.toByteArray(bytes,
- TOTAL_BATTLES_LOST_IDX, ByteOrder.BIG_ENDIAN)
- bytes[WIN_PCT_IDX] = beCharacter.getWinPercentage()
- bytes[NEXT_ADVENTURE_MISSION_STAGE_IDX] = beCharacter.nextAdventureMissionStage
- bytes[MOOD_IDX] = beCharacter.mood
- bytes[ACTIVITY_LEVEL_IDX] = beCharacter.activityLevel
- bytes[HEART_RATE_CURRENT_IDX] = beCharacter.heartRateCurrent.toByte()
- beCharacter.vitalPoints.toByteArray(bytes, VITAL_POINTS_IDX, ByteOrder.BIG_ENDIAN)
- bytes[ITEM_EFFECT_MENTAL_STATE_VALUE_IDX] = beCharacter.itemEffectMentalStateValue
- bytes[ITEM_EFFECT_MENTAL_STATE_MINUTES_REMAINING_IDX] = beCharacter.itemEffectMentalStateMinutesRemaining
- bytes[ITEM_EFFECT_ACTIVITY_LEVEL_VALUE_IDX] = beCharacter.itemEffectActivityLevelValue
- bytes[ITEM_EFFECT_ACTIVITY_LEVEL_MINUTES_REMAINING_IDX] = beCharacter.itemEffectActivityLevelMinutesRemaining
- bytes[ITEM_EFFECT_VITAL_POINTS_CHANGE_VALUE_IDX] = beCharacter.itemEffectVitalPointsChangeValue
- bytes[ITEM_EFFECT_VITAL_POINTS_CHANGE_MINUTES_REMAINING_IDX] = beCharacter.itemEffectVitalPointsChangeMinutesRemaining
- beCharacter.transformationCountdown.toByteArray(bytes,
- TRANSFORMATION_COUNT_DOWN_IDX, ByteOrder.BIG_ENDIAN)
- transformationHistoryToByteArray(beCharacter.transformationHistory, bytes)
- beCharacter.trainingHp.toByteArray(bytes, TRAINING_HP_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.trainingAp.toByteArray(bytes, TRAINING_AP_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.trainingBp.toByteArray(bytes, TRAINING_BP_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.remainingTrainingTimeInMinutes.toByteArray(bytes,
- TRAINING_TIME_IDX, ByteOrder.BIG_ENDIAN)
- bytes[ABILITY_RARITY_IDX] = beCharacter.abilityRarity.ordinal.toByte()
- beCharacter.abilityType.toByteArray(bytes, ABILITY_TYPE_IDX, ByteOrder.BIG_ENDIAN)
- beCharacter.abilityBranch.toByteArray(bytes, ABILITY_BRANCH_IDX, ByteOrder.BIG_ENDIAN)
- bytes[ABILITY_RESET_IDX] = beCharacter.abilityReset
- bytes[RANK_IDX] = beCharacter.rank
- bytes[ITEM_TYPE_IDX] = beCharacter.itemType
- bytes[ITEM_MULTIPLIER_IDX] = beCharacter.itemMultiplier
- bytes[ITEM_REMAINING_TIME_IDX] = beCharacter.itemRemainingTime
- beCharacter.otp0.copyInto(bytes, OTP_START_IDX, 0, beCharacter.otp0.size)
- beCharacter.otp1.copyInto(bytes, OTP2_START_IDX, 0, beCharacter.otp1.size)
- bytes[CHARACTER_CREATION_FIRMWARE_VERSION_IDX] = beCharacter.characterCreationFirmwareVersion.majorVersion
- bytes[CHARACTER_CREATION_FIRMWARE_VERSION_IDX+1] = beCharacter.characterCreationFirmwareVersion.minorVersion
- }
-
- // finalizeByteArrayFormat finalizes the byte array for BE NFC format by setting all the
- // checksums, and duplicating the duplicate memory pages.
- override fun finalizeByteArrayFormat(bytes: ByteArray) {
- checksumCalculator.recalculateChecksums(bytes)
- performPageBlockDuplications(bytes)
- }
-
- override fun getOperationCommandBytes(header: NfcHeader, operation: Byte): ByteArray {
- return byteArrayOf(TagCommunicator.NFC_WRITE_COMMAND, OPERATION_PAGE, header.status, operation, header.dimIdBytes[0], header.dimIdBytes[1])
- }
-
- // parses a BENfcCharacter from a ByteArray produced by the TagCommunicator
- override fun parseNfcCharacter(bytes: ByteArray): BENfcCharacter {
- return BENfcCharacter(
- appReserved1 = bytes.sliceArray(APP_RESERVED_START..<(APP_RESERVED_START + APP_RESERVED_SIZE)),
- injuryStatus = NfcCharacter.InjuryStatus.entries[bytes.getUInt16(INJURY_STATUS_IDX, ByteOrder.BIG_ENDIAN).toInt()],
- appReserved2 = bytes.copyIntoUShortArray(APP_RESERVED_2_START, APP_RESERVED_2_SIZE),
- charIndex = bytes.getUInt16(CHARACTER_INDEX_IDX, ByteOrder.BIG_ENDIAN),
- dimId = bytes.getUInt16(DIM_ID_IDX, ByteOrder.BIG_ENDIAN),
- stage = bytes[PHASE_IDX],
- attribute = NfcCharacter.Attribute.entries[bytes[ATTRIBUTE_IDX].toInt()],
- ageInDays = bytes[AGE_IN_DAYS_IDX],
- trainingPp = bytes.getUInt16(TRAINING_PP_IDX, ByteOrder.BIG_ENDIAN),
- currentPhaseBattlesWon = bytes.getUInt16(CURRENT_BATTLES_WON_IDX, ByteOrder.BIG_ENDIAN),
- currentPhaseBattlesLost = bytes.getUInt16(CURRENT_BATTLES_LOST_IDX, ByteOrder.BIG_ENDIAN),
- totalBattlesWon = bytes.getUInt16(TOTAL_BATTLES_WON_IDX, ByteOrder.BIG_ENDIAN),
- totalBattlesLost = bytes.getUInt16(TOTAL_BATTLES_LOST_IDX, ByteOrder.BIG_ENDIAN),
- nextAdventureMissionStage = bytes[NEXT_ADVENTURE_MISSION_STAGE_IDX],
- mood = bytes[MOOD_IDX],
- activityLevel = bytes[ACTIVITY_LEVEL_IDX],
- heartRateCurrent = bytes[HEART_RATE_CURRENT_IDX].toUByte(),
- vitalPoints = bytes.getUInt16(VITAL_POINTS_IDX, ByteOrder.BIG_ENDIAN),
- itemEffectMentalStateValue = bytes[ITEM_EFFECT_MENTAL_STATE_VALUE_IDX],
- itemEffectMentalStateMinutesRemaining = bytes[ITEM_EFFECT_MENTAL_STATE_MINUTES_REMAINING_IDX],
- itemEffectActivityLevelValue = bytes[ITEM_EFFECT_ACTIVITY_LEVEL_VALUE_IDX],
- itemEffectActivityLevelMinutesRemaining = bytes[ITEM_EFFECT_ACTIVITY_LEVEL_MINUTES_REMAINING_IDX],
- itemEffectVitalPointsChangeValue = bytes[ITEM_EFFECT_VITAL_POINTS_CHANGE_VALUE_IDX],
- itemEffectVitalPointsChangeMinutesRemaining = bytes[ITEM_EFFECT_VITAL_POINTS_CHANGE_MINUTES_REMAINING_IDX],
- transformationCountdownInMinutes = bytes.getUInt16(TRANSFORMATION_COUNT_DOWN_IDX, ByteOrder.BIG_ENDIAN),
- transformationHistory = buildTransformationHistory(bytes),
- trainingHp = bytes.getUInt16(TRAINING_HP_IDX, ByteOrder.BIG_ENDIAN),
- trainingAp = bytes.getUInt16(TRAINING_AP_IDX, ByteOrder.BIG_ENDIAN),
- trainingBp = bytes.getUInt16(TRAINING_BP_IDX, ByteOrder.BIG_ENDIAN),
- remainingTrainingTimeInMinutes = bytes.getUInt16(TRAINING_TIME_IDX, ByteOrder.BIG_ENDIAN),
- abilityRarity = NfcCharacter.AbilityRarity.entries[bytes[ABILITY_RARITY_IDX].toInt()],
- abilityType = bytes.getUInt16(ABILITY_TYPE_IDX, ByteOrder.BIG_ENDIAN),
- abilityBranch = bytes.getUInt16(ABILITY_BRANCH_IDX, ByteOrder.BIG_ENDIAN),
- abilityReset = bytes[ABILITY_RESET_IDX],
- rank = bytes[RANK_IDX],
- itemType = bytes[ITEM_TYPE_IDX],
- itemMultiplier = bytes[ITEM_MULTIPLIER_IDX],
- itemRemainingTime = bytes[ITEM_REMAINING_TIME_IDX],
- otp0 = bytes.sliceArray(OTP_START_IDX..OTP_END_IDX),
- otp1 = bytes.sliceArray(OTP2_START_IDX..OTP2_END_IDX),
- characterCreationFirmwareVersion = FirmwareVersion(
- majorVersion = bytes[CHARACTER_CREATION_FIRMWARE_VERSION_IDX],
- minorVersion = bytes[CHARACTER_CREATION_FIRMWARE_VERSION_IDX+1]),
- )
- }
-
- override fun parseHeader(headerBytes: ByteArray): NfcHeader {
- Log.i(TagCommunicator.TAG, "Bytes in header: ${headerBytes.size}")
- val header = NfcHeader(
- deviceId = DeviceType.VitalBraceletBEDeviceType,
- deviceSubType = DeviceSubType.Original,
- vbCompatibleTagIdentifier = headerBytes.sliceArray(0..3), // this is a magic number used to verify that the tag is a VB.
- status = headerBytes[8],
- operation = headerBytes[9],
- dimIdBytes = headerBytes.sliceArray(10..11),
- appFlag = headerBytes[12],
- nonce = headerBytes.sliceArray(13..15)
- )
- Log.i(TagCommunicator.TAG, "Header: $header")
- return header
- }
-
- // a block being 4 pages
- private val firstIndicesOfBlocksToCopy = intArrayOf(32, 64, 96, 128, 256, 416)
- private fun performPageBlockDuplications(data: ByteArray) {
- for (firstIndex in firstIndicesOfBlocksToCopy) {
- for (i in firstIndex..firstIndex + 15) {
- data[i+16] = data[i]
- }
- }
- }
-
- private fun transformationHistoryToByteArray(transformationHistory: Array<NfcCharacter.Transformation>, bytes: ByteArray) {
- if (transformationHistory.size != 8) {
- throw IllegalArgumentException("Transformation History must be exactly size 8")
- }
- for (phase in 0..<transformationHistory.size) {
- var rootIdx = phase*4 + TRANSFORMATION_HISTORY_START
- if (phase > 2) {
- rootIdx += 4 // we skip 220-223 for some reason
- }
- if (phase > 5) {
- rootIdx += 4 // we skip 236-239 for some reason
- }
- bytes[rootIdx] = transformationHistory[phase].toCharIndex
- bytes[rootIdx+1] = transformationHistory[phase].yearsSince1988
- bytes[rootIdx+2] = transformationHistory[phase].month
- bytes[rootIdx+3] = transformationHistory[phase].day
- }
- }
-
- private fun buildTransformationHistory(data: ByteArray): Array<NfcCharacter.Transformation> {
- val transformationHistory = Array<NfcCharacter.Transformation>(8) { phase ->
- var rootIdx = phase*4 + TRANSFORMATION_HISTORY_START
- if (phase > 2) {
- rootIdx += 4 // we skip 220-223 for some reason
- }
- if (phase > 5) {
- rootIdx += 4 // we skip 236-239 for some reason
- }
- NfcCharacter.Transformation(
- toCharIndex = data[rootIdx],
- yearsSince1988 = data[rootIdx+1],
- month = data[rootIdx+2],
- day = data[rootIdx+3]
- )
- }
- return transformationHistory
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDevice.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/BENfcDevice.kt +0 -20
@@ 1,20 0,0 @@
-package com.github.cfogrady.vbnfc.be
-
-import com.github.cfogrady.vbnfc.data.NfcDevice
-import java.util.BitSet
-
-class BENfcDevice(
- val gender: Gender,
- val registedDims: ByteArray,
- val currDays: Byte,
-
-
- ):
- NfcDevice(
- BitSet(),
- ) {
- enum class Gender {
- Male,
- Female
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/FirmwareVersion.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/be/FirmwareVersion.kt +0 -3
@@ 1,3 0,0 @@
-package com.github.cfogrady.vbnfc.be
-
-data class FirmwareVersion(val majorVersion: Byte, val minorVersion: Byte)>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/DeviceSubType.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/DeviceSubType.kt +0 -12
@@ 1,12 0,0 @@
-package com.github.cfogrady.vbnfc.data
-
-class DeviceSubType {
- companion object {
- const val Original: UShort = 1u
- const val FirstRevision: UShort = 2u
- const val SecondRevision: UShort = 3u
- const val DigiviceV: UShort = 4u
- const val DigiviceVSecondRevision: UShort = 5u
- const val VitalHero: UShort = 6u
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/DeviceType.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/DeviceType.kt +0 -9
@@ 1,9 0,0 @@
-package com.github.cfogrady.vbnfc.data
-
-class DeviceType {
- companion object {
- const val VitalSeriesDeviceType: UShort = 2u
- const val VitalCharactersDeviceType: UShort = 3u
- const val VitalBraceletBEDeviceType: UShort = 4u
- }
-}
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcCharacter.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcCharacter.kt +0 -147
@@ 1,147 0,0 @@
-package com.github.cfogrady.vbnfc.data
-
-import java.util.Objects
-
-open class NfcCharacter(
- val dimId: UShort,
- var charIndex: UShort,
- var stage: Byte,
- var attribute: Attribute,
- var ageInDays: Byte,
- var nextAdventureMissionStage: Byte, // next adventure mission stage on the character's dim
- var mood: Byte,
- var vitalPoints: UShort,
- var transformationCountdown: UShort,
- var injuryStatus: InjuryStatus,
- var trophies: UShort,
- var currentPhaseBattlesWon: UShort,
- var currentPhaseBattlesLost: UShort,
- var totalBattlesWon: UShort,
- var totalBattlesLost: UShort,
- var activityLevel: Byte,
- var heartRateCurrent: UByte,
- var transformationHistory: Array<Transformation>
-) {
-
- data class Transformation(
- val toCharIndex: Byte,
- val yearsSince1988: Byte,
- val month: Byte,
- val day: Byte)
-
- enum class AbilityRarity {
- None,
- Common,
- Rare,
- SuperRare,
- SuperSuperRare,
- UltraRare,
- }
-
- enum class Attribute {
- None,
- Virus,
- Data,
- Vaccine,
- Free
- }
- enum class InjuryStatus {
- None,
- Injury,
- InjuryHealed,
- InjuryTwo,
- InjuryTwoHealed,
- InjuryThree,
- InjuryThreeHealed,
- InjuryFour,
- }
-
- fun getTransformationHistoryString(separator: String = System.lineSeparator()): String {
- val builder = StringBuilder()
- for(i in transformationHistory.indices) {
- builder.append(transformationHistory[i])
- if(i != transformationHistory.size-1) {
- builder.append(separator)
- }
- }
- return builder.toString()
- }
-
-
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as NfcCharacter
-
- if (dimId != other.dimId) return false
- if (charIndex != other.charIndex) return false
- if (stage != other.stage) return false
- if (attribute != other.attribute) return false
- if (ageInDays != other.ageInDays) return false
- if (nextAdventureMissionStage != other.nextAdventureMissionStage) return false
- if (mood != other.mood) return false
- if (vitalPoints != other.vitalPoints) return false
- if (transformationCountdown != other.transformationCountdown) return false
- if (injuryStatus != other.injuryStatus) return false
- if (trophies != other.trophies) return false
- if (currentPhaseBattlesWon != other.currentPhaseBattlesWon) return false
- if (currentPhaseBattlesLost != other.currentPhaseBattlesLost) return false
- if (totalBattlesWon != other.totalBattlesWon) return false
- if (totalBattlesLost != other.totalBattlesLost) return false
- if (activityLevel != other.activityLevel) return false
- if (heartRateCurrent != other.heartRateCurrent) return false
- if (!transformationHistory.contentEquals(other.transformationHistory)) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return Objects.hash(
- dimId,
- charIndex,
- stage,
- attribute,
- ageInDays,
- nextAdventureMissionStage,
- mood,
- vitalPoints,
- transformationCountdown,
- injuryStatus,
- trophies,
- currentPhaseBattlesWon,
- currentPhaseBattlesLost,
- totalBattlesWon,
- totalBattlesLost,
- activityLevel,
- heartRateCurrent,
- transformationHistory.contentHashCode()
- )
- }
-
- override fun toString(): String {
- return """NfcCharacter(
- dimId=$dimId,
- charIndex=$charIndex,
- stage=$stage,
- attribute=$attribute,
- ageInDays=$ageInDays,
- nextAdventureMissionStage=$nextAdventureMissionStage,
- mood=$mood,
- vitalPoints=$vitalPoints,
- transformationCountdown=$transformationCountdown,
- injuryStatus=$injuryStatus,
- trophies=$trophies,
- currentPhaseBattlesWon=$currentPhaseBattlesWon,
- currentPhaseBattlesLost=$currentPhaseBattlesLost,
- totalBattlesWon=$totalBattlesWon,
- totalBattlesLost=$totalBattlesLost,
- activityLevel=$activityLevel,
- heartRateCurrent=$heartRateCurrent,
- transformationHistory=${transformationHistory.contentToString()}
-)"""
- }
-
-
-}
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcData.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcData.kt +0 -3
@@ 1,3 0,0 @@
-package com.github.cfogrady.vbnfc.data
-
-class NfcData(val nfcCharacter: NfcCharacter, val nfcDevice: NfcDevice)
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcDevice.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcDevice.kt +0 -9
@@ 1,9 0,0 @@
-package com.github.cfogrady.vbnfc.data
-
-import java.util.BitSet
-
-open class NfcDevice(private val registeredDims: BitSet) {
- fun isDimRegistered(dimId: UShort): Boolean {
- return registeredDims[dimId.toInt()]
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcHeader.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/data/NfcHeader.kt +0 -26
@@ 1,26 0,0 @@
-package com.github.cfogrady.vbnfc.data
-
-import com.github.cfogrady.vbnfc.NfcDataTranslator
-import com.github.cfogrady.vbnfc.getUInt16
-import com.github.cfogrady.vbnfc.toByteArray
-import java.nio.ByteOrder
-
-open class NfcHeader (
- val deviceId: UShort,
- val deviceSubType: UShort,
- val vbCompatibleTagIdentifier: ByteArray, // this is a magic number used to verify that the tag is a VB.
- val status: Byte,
- val operation: Byte,
- var dimIdBytes: ByteArray,
- val appFlag: Byte,
- val nonce: ByteArray,
-) {
-
- fun getDimId(): UShort {
- return dimIdBytes.getUInt16(0, ByteOrder.BIG_ENDIAN)
- }
-
- fun setDimId(dimId: UShort) {
- dimIdBytes = dimId.toByteArray(ByteOrder.BIG_ENDIAN)
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/vb/VBNfcDataTranslator.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/vb/VBNfcDataTranslator.kt +0 -45
@@ 1,45 0,0 @@
-package com.github.cfogrady.vbnfc.vb
-
-import com.github.cfogrady.vbnfc.CryptographicTransformer
-import com.github.cfogrady.vbnfc.NfcDataTranslator
-import com.github.cfogrady.vbnfc.TagCommunicator
-import com.github.cfogrady.vbnfc.data.DeviceSubType
-import com.github.cfogrady.vbnfc.data.DeviceType
-import com.github.cfogrady.vbnfc.data.NfcCharacter
-import com.github.cfogrady.vbnfc.data.NfcHeader
-import com.github.cfogrady.vbnfc.getUInt16
-
-class VBNfcDataTranslator(override val cryptographicTransformer: CryptographicTransformer) : NfcDataTranslator {
-
- companion object {
- const val OPERATION_PAGE: Byte = 0x6
- }
-
- override fun finalizeByteArrayFormat(bytes: ByteArray) {
- TODO("Not yet implemented")
- }
-
- override fun getOperationCommandBytes(header: NfcHeader, operation: Byte): ByteArray {
- val vbHeader = header as VBNfcHeader
- return byteArrayOf(TagCommunicator.NFC_WRITE_COMMAND, OPERATION_PAGE, header.status, header.dimIdBytes[1], operation, vbHeader.reserved)
- }
-
- override fun parseNfcCharacter(bytes: ByteArray): NfcCharacter {
- TODO("Not yet implemented")
- }
-
- override fun parseHeader(headerBytes: ByteArray): NfcHeader {
- val header = VBNfcHeader(
- deviceType = DeviceType.VitalSeriesDeviceType,
- deviceSubType = headerBytes.getUInt16(6),
- vbCompatibleTagIdentifier = headerBytes.sliceArray(0..3), // this is a magic number used to verify that the tag is a VB.
- status = headerBytes[8],
- dimId = headerBytes[9],
- operation = headerBytes[10],
- reserved = headerBytes[11],
- appFlag = headerBytes[12],
- nonce = headerBytes.sliceArray(13..15)
- )
- return header
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/vb/VBNfcHeader.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/vb/VBNfcHeader.kt +0 -25
@@ 1,25 0,0 @@
-package com.github.cfogrady.vbnfc.vb
-
-import com.github.cfogrady.vbnfc.data.DeviceType
-import com.github.cfogrady.vbnfc.data.NfcHeader
-
-class VBNfcHeader(
- deviceType: UShort,
- deviceSubType: UShort,
- vbCompatibleTagIdentifier: ByteArray,
- status: Byte,
- operation: Byte,
- dimId: Byte,
- val reserved: Byte,
- appFlag: Byte,
- nonce: ByteArray
- ) : NfcHeader(
- deviceId = deviceType,
- deviceSubType = deviceSubType,
- vbCompatibleTagIdentifier = vbCompatibleTagIdentifier,
- status = status,
- operation = operation,
- dimIdBytes = byteArrayOf(0, dimId),
- appFlag = appFlag,
- nonce = nonce,
-)>
\ No newline at end of file
D vb-nfc-reader/src/main/res/values/arrays.xml => vb-nfc-reader/src/main/res/values/arrays.xml +0 -4
@@ 1,4 0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string-array name="substitutionArray" />
-</resources>>
\ No newline at end of file
D vb-nfc-reader/src/main/res/values/strings.xml => vb-nfc-reader/src/main/res/values/strings.xml +0 -6
@@ 1,6 0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="password1" />
- <string name="password2" />
- <string name="decryptionKey" />
-</resources>>
\ No newline at end of file
D vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/CryptographicTransformerHelper.kt => vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/CryptographicTransformerHelper.kt +0 -50
@@ 1,50 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import java.nio.charset.StandardCharsets
-import java.util.Base64
-import javax.crypto.Cipher
-import javax.crypto.spec.IvParameterSpec
-import javax.crypto.spec.SecretKeySpec
-import kotlin.random.Random
-
-// This class allows for creating new test keys
-class CryptographicTransformerHelper {
-
- companion object {
- fun generateAesKey(): String {
- val combinedKey = ByteArray(24)
- for (i in combinedKey.indices) {
- combinedKey[i] = Random.nextInt(48, 58 + 26).toByte()
- if(combinedKey[i] > 57) {
- combinedKey[i] = (combinedKey[i] + 7).toByte()
- }
- }
- return combinedKey.toString(StandardCharsets.UTF_8)
- }
-
- fun generateHMacKey(aesKey: String, hmacKey: String = generateRandomPlainTextHmacKey()): String {
- val hmacKeyData = hmacKey.toByteArray(StandardCharsets.UTF_8)
- val encryptedHmacKey = encryptAesCbcPkcs5Padding(aesKey, hmacKeyData)
- return Base64.getEncoder().encodeToString(encryptedHmacKey)
- }
-
- private fun generateRandomPlainTextHmacKey(): String {
- val key = ByteArray(4)
- for (i in key.indices) {
- key[i] = Random.nextInt(33, 126).toByte()
- }
- return key.toString(StandardCharsets.UTF_8)
- }
-
- private fun encryptAesCbcPkcs5Padding(key: String, data: ByteArray): ByteArray {
- val keyBytes = key.toByteArray(StandardCharsets.UTF_8)
- val rightSizedKey = keyBytes.copyOf(32)
- val ivBytes = keyBytes.copyOfRange(key.length - 16, key.length)
- val secretKeySpec = SecretKeySpec(rightSizedKey, "AES")
- val ivParameterSpec = IvParameterSpec(ivBytes)
- val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
- cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
- return cipher.doFinal(data)
- }
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/CryptographicTransformerTest.kt => vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/CryptographicTransformerTest.kt +0 -56
@@ 1,56 0,0 @@
-package com.github.cfogrady.vbnfc
-
-import io.mockk.every
-import io.mockk.mockkStatic
-import org.junit.Assert
-import org.junit.Test
-
-class CryptographicTransformerTest {
-
- val testTagId = byteArrayOf(0x04, 0x40, 0xaf.toByte(), 0xa2.toByte(), 0xee.toByte(), 0x0f, 0x90.toByte())
-
- val testAesKey = "8A4PEGIXJS454EFRTX9F5PCT"
- val testHmacKey1 = "40nz2LdPI99D+x748XmQmw=="
- val testHmacKey2 = "5Jz9lWtNg28qxqIBoR5kLw=="
- val testSubstitutionCipher = intArrayOf(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
-
- @OptIn(ExperimentalStdlibApi::class)
- @Test
- fun createPasswordCreatesExpectedPassword() {
-
- mockkStatic(android.util.Log::class)
- every { android.util.Log.i(any<String>(), any<String>()) } answers {
- val message = it.invocation.args[1] as String
- println(message)
- 1
- }
-
- val cryptographicTransformer = CryptographicTransformer(testHmacKey1, testHmacKey2, testAesKey, testSubstitutionCipher)
-
- val result = cryptographicTransformer.createNfcPassword(testTagId)
- val expected = "a3c83dd7"
-
- Assert.assertEquals(expected, result.toHexString())
- }
-
- @OptIn(ExperimentalStdlibApi::class)
- @Test
- fun dataEncryptionAndDecryptionWorks() {
- val cryptographicTransformer = CryptographicTransformer(testHmacKey1, testHmacKey2, testAesKey, testSubstitutionCipher)
-
- val characters = listOf(
- "000000000000000000000000000000000000000000000000000000000000000010400010040010001000000000001094104000100400100010000000000010940000000000000000000400840203008d0000000000000000000400840203008d00000006000300060003000001010014000000060003000600030000010100140156025309850000000000000000d6100156025309850000000000000000d610000000000000280000000000000000280000000000000000030c08302402279424022624022524022424022324020151002402290124022904240229000000f2ffffffffffffffffffffffff000000f4ffffffffffffffff00000000000000f80000000000000000000014860000009a0000000000000000000014860000009a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101010101010101000000000000008702020202020202020000000001010033000000000000000000000000000000000000000000000000000000000000000014c5400000000000000000000000001914c540000000000000000000000000190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000000000000000000000000000000000000000000000000000000000000010400010040010001000000000001094104000100400100010000000000010940000000000000000000500820302008c0000000000000000000500820302008c000b00030002000b000b000001010028000b00030002000b000b0000010100280464028b04b4000000000000000308b80464028b04b4000000000000000308b80474000000001500000000000000008d0002000000000000000000002401062d240105240104240103240102240101c80024010601240106042401060000008605240116ffffffffffffffff00000038ffffffffffffffff00000000000000f80005000f000a00000000046b0000008d0005000f000a00000000046b0000008d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030303030303030300000000000000210404040404040404000000000101003a000000000000000000000000000000000000000000000000000000000000000014c5400000000000000000000000001914c540000000000000000000000000190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
- )
- val expectedEncryptions = listOf(
- "74b46e2d78292ce251a84e644f5451dd4aa2c6d782a9aeef2c3229103c7a26e5d9c2c53d2f63077a1cf9ae18efc382edca9e50d7d861e2927cd9df2494a0772ff1b8791bae7883a862d218559277db6a9665f18da0f1caa92ccc903dbc80e66367ee39b2f243cc0b5bfcc2131dfeb9d361179d4033517c4ebbceea15ab9bfb27a9a05cb45e5dfe37287a010db3bcfd9a6f1cc54a31ea7da193dbd341e83997ad80713d5fd19c7d76a6dbedfb25049ab5472e4094d0949f19c853b2ac6c6827412904fd1683fae61ea1a64508e9243e04bdfdee63ff98321204fd4fd2a17112c0bd5aa7c11986ea7bc00110dedea2d0644ea5ea3f6e928f8c6c98107eeedbfd1b774d07df237dc294f3b8fccaa8ab2e1c59de2c695f367214f55accc2e43ca0b2c021161d3c8b9ea2405717d6884d089d848d82b37a9ed711a8e9809336d05bab0091face97671e9260d4ae741e94ef1f8d950ff390dd05bfa28adb6b72a8214192be530af26db6abda69dce0707d3d92f44beb1f01f24d7c1123ad25d37349bf7d5777723f1acb56d2f96ba435ffa505c3ae3d270c85bcb64aabac0d15d8128f7fe34cc303a74f8d42871a25d452a1ef033b824b17e51b808b5363f2f1da5a57c3fcc881b57028e76155f6d49e35e43bd142747c3a4a14217fa8600a6e0d3cdbc6b5598510023ad0897d38ca04e78a78e198ee76e784d6337ad8fa65e4b617ea11433fb3d9d7d3269a7b5e1ff9a4cd723f3fc4f81768816619fadf21b5276f31d704bba1a3a9afb0363cca3ddb272bdced2d252e21824d2828b7c36cdd37202cd5a6b5224e505f87188d3c63af33c8916aa8ae0116dd1028c370236591a4413559fe40d14dcdf72959a122def9b4c6cf5d928142a66b0dcf0ffa5d447b53a8661e3578a49d632e0285d180814116a3e78fc00fc01106a248af8afa329a69054b827e41a62abdc65074554fa2f07b773d56bae73252efa374f47397c1d36e056eb9d32b6dcf18f40669a5b23723c475c50c5ba4154b550c67dd90d4a6919686c69fdcfd7d4d988a0df5e420b6240068ae8aa07c88c38bac3b6ce7a87c8df0f540cc3c5be2180b70127c523102ed9be2cfb25a032089d47d05db6dbde2758239ad54b94756906922ed7e7c4ecf6b256c658417fa8003100c72de1dee627c1a0c670dbc91596e4dc0d4393eb24884b41979e8ff3d5b0583416750d1d856a2a689cd",
- "74b46e2d78292ce251a84e644f5451dd4aa2c6d782a9aeef2c3229103c7a26e5d9c2c53d2f63077a1cf9ae18efc382edca9e50d7d861e2927cd9df2494a0772ff1b8791bae7883a862d318539376db6b9665f18da0f1caa92ccd903bbd81e66267e539b7f242cc065bf4c2131dfeb9ef611c9d4533507c43bbc6ea15ab9bfb1bac925c6c536cfe37287a010db3bf23326a2ec5923cdb7da193dbd341e83a490584053d5fd19c4076a6dbedfb25049a10472c4094d0949f19cb5fba9c6c6b06f82907de1680dbe61d86a64629e9273e9dbdfded4cff98313d04fd4cfda17112b4478159281986ea7bc00110dedea2d0a84ea5ea3f6e928f8c6c98107eeedbfd1b774807d02377c294f3b8ec27a8ab2e0b59db2c665f3c7214f55adc2fe43ca0a5c021161d3c8b9ea2405717d6884d089d848d82b37a9ed711a8e9809336d05bab0091face97671e9260d4ae741e94ef1f8d950ff390dd05bfa28adb6b72a8214190bc5108f06fb4a9da69dce0707d3d34f24ded1907f44b7a1123ad25d37349b67d5777723f1acb56d2f96ba435ffa505c3ae3d270c85bcb64aabac0d15d8128f7fe34cc303a74f8d42871a25d452a1ef033b824b17e51b808b5363f2f1da5a57c3fcc881b57028e76155f6d49e35e43bd142747c3a4a14217fa8600a6e0d3cdbc6b5598510023ad0897d38ca04e78a78e198ee76e784d6337ad8fa65e4b617ea11433fb3d9d7d3269a7b5e1ff9a4cd723f3fc4f81768816619fadf21b5276f31d704bba1a3a9afb0363cca3ddb272bdced2d252e21824d2828b7c36cdd37202cd5a6b5224e505f87188d3c63af33c8916aa8ae0116dd1028c370236591a4413559fe40d14dcdf72959a122def9b4c6cf5d928142a66b0dcf0ffa5d447b53a8661e3578a49d632e0285d180814116a3e78fc00fc01106a248af8afa329a69054b827e41a62abdc65074554fa2f07b773d56bae73252efa374f47397c1d36e056eb9d32b6dcf18f40669a5b23723c475c50c5ba4154b550c67dd90d4a6919686c69fdcfd7d4d988a0df5e420b6240068ae8aa07c88c38bac3b6ce7a87c8df0f540cc3c5be2180b70127c523102ed9be2cfb25a032089d47d05db6dbde2758239ad54b94756906922ed7e7c4ecf6b256c658417fa8003100c72de1dee627c1a0c670dbc91596e4dc0d4393eb24884b41979e8ff3d5b0583416750d1d856a2a689cd",
- )
- for (i in characters.indices) {
- val encrypted = cryptographicTransformer.encryptData(characters[i].hexToByteArray(), testTagId)
- Assert.assertEquals(expectedEncryptions[i], encrypted.toHexString())
- val decrypted = cryptographicTransformer.decryptData(encrypted, testTagId)
- Assert.assertEquals(characters[i], decrypted.toHexString())
- }
- }
-}>
\ No newline at end of file
D vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/be/BENfcDataTranslatorTest.kt => vb-nfc-reader/src/test/java/com/github/cfogrady/vbnfc/be/BENfcDataTranslatorTest.kt +0 -73
@@ 1,73 0,0 @@
-package com.github.cfogrady.vbnfc.be
-
-import com.github.cfogrady.vbnfc.ChecksumCalculator
-import com.github.cfogrady.vbnfc.CryptographicTransformer
-import com.github.cfogrady.vbnfc.data.NfcCharacter
-import io.mockk.mockkClass
-import org.junit.Assert
-import org.junit.Test
-
-class BENfcDataTranslatorTest {
- @OptIn(ExperimentalStdlibApi::class)
- @Test
- fun testNfcCharacterParsing() {
- val nfcBytes = "000000000000000000000000000000000000000000000000000000000000000010400010040010001000000000001094104000100400100010000000000010940000000000000000000500820302008c0000000000000000000500820302008c000b00030002000b000b000001010028000b00030002000b000b0000010100280464028b04b4000000000000000308b80464028b04b4000000000000000308b80474000000001500000000000000008d0002000000000000000000002401062d240105240104240103240102240101c80024010601240106042401060000008605240116ffffffffffffffff00000038ffffffffffffffff00000000000000f80005000f000a00000000046b0000008d0005000f000a00000000046b0000008d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010101010101010100000000000000210202020202020202000000000101003a000000000000000000000000000000000000000000000000000000000000000014c5400000000000000000000000001914c540000000000000000000000000190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".hexToByteArray()
- val mockCryptographicTransformer = mockkClass(CryptographicTransformer::class)
- val checksumCalculator = ChecksumCalculator()
- val beNfcDataTranslator = BENfcDataTranslator(mockCryptographicTransformer, checksumCalculator)
-
- val character = beNfcDataTranslator.parseNfcCharacter(nfcBytes)
- val expectedCharacter = BENfcCharacter(
- dimId = 130u,
- charIndex = 5u,
- stage = 3,
- attribute = NfcCharacter.Attribute.Data,
- ageInDays = 0,
- mood = 100,
- characterCreationFirmwareVersion = FirmwareVersion(1, 1),
- nextAdventureMissionStage = 4,
- vitalPoints = 1204u,
- transformationCountdownInMinutes = 776u,
- injuryStatus = NfcCharacter.InjuryStatus.None,
- trainingPp = 11u,
- currentPhaseBattlesWon = 3u,
- currentPhaseBattlesLost = 2u,
- totalBattlesWon = 11u,
- totalBattlesLost = 11u,
- activityLevel = 2,
- heartRateCurrent = 139u,
- transformationHistory = arrayOf(NfcCharacter.Transformation(0, 36, 1, 6),
- NfcCharacter.Transformation(1, 36, 1, 6),
- NfcCharacter.Transformation(4, 36, 1, 6),
- NfcCharacter.Transformation(5, 36, 1, 22),
- NfcCharacter.Transformation(-1, -1, -1, -1),
- NfcCharacter.Transformation(-1, -1, -1, -1),
- NfcCharacter.Transformation(-1, -1, -1, -1),
- NfcCharacter.Transformation(-1, -1, -1, -1),
- ),
- trainingHp = 5u,
- trainingAp = 15u,
- trainingBp = 10u,
- remainingTrainingTimeInMinutes = 1131u,
- itemEffectMentalStateValue = 0,
- itemEffectMentalStateMinutesRemaining = 0,
- itemEffectActivityLevelValue = 0,
- itemEffectActivityLevelMinutesRemaining = 0,
- itemEffectVitalPointsChangeValue = 0,
- itemEffectVitalPointsChangeMinutesRemaining = 0,
- abilityRarity = NfcCharacter.AbilityRarity.None,
- abilityType = 0u,
- abilityBranch = 0u,
- abilityReset = 0,
- rank = 0,
- itemType = 0,
- itemMultiplier = 0,
- itemRemainingTime = 0,
- appReserved1 = byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- appReserved2 = arrayOf(0u, 0u, 0u),
- otp0 = "0101010101010101".hexToByteArray(),
- otp1 = "0202020202020202".hexToByteArray()
- )
- Assert.assertEquals(expectedCharacter, character)
- }
-}>
\ No newline at end of file