From 6146f1c4098252825d6a88ecfc35fd446d7bcfb3 Mon Sep 17 00:00:00 2001 From: ppc Date: Fri, 30 Jan 2026 22:16:58 +0000 Subject: [PATCH] remove physical card scanning (enables sideloading) --- amnet-native/App.tsx | 18 +++-- amnet-native/app.json | 17 +--- amnet-native/app/ServerCardIn.tsx | 74 ++--------------- amnet-native/app/card/CardAddModal.tsx | 32 +------- amnet-native/app/card/PhyscialCardScan.tsx | 87 -------------------- amnet-native/lib/iccard-crypto.ts | 49 ----------- amnet-native/lib/iccard.ts | 94 ---------------------- amnet-native/package-lock.json | 32 ++------ amnet-native/package.json | 3 +- amnet-shared/package-lock.json | 6 +- readme.md | 13 +-- 11 files changed, 42 insertions(+), 383 deletions(-) delete mode 100644 amnet-native/app/card/PhyscialCardScan.tsx delete mode 100644 amnet-native/lib/iccard-crypto.ts diff --git a/amnet-native/App.tsx b/amnet-native/App.tsx index 9202455..6d44af5 100644 --- a/amnet-native/App.tsx +++ b/amnet-native/App.tsx @@ -77,15 +77,17 @@ const Tab = createBottomTabNavigator(); const Stack = createNativeStackNavigator(); function AppContent() { - return ( } screenOptions={{headerShown: false}}> - ()}}/> + return ( + } screenOptions={{headerShown: false}}> + ()}}/> - ()}}/> - ) + ()}}/> + + ) } export default function AppRoot() { diff --git a/amnet-native/app.json b/amnet-native/app.json index 8295f61..4cf1955 100644 --- a/amnet-native/app.json +++ b/amnet-native/app.json @@ -10,25 +10,12 @@ "ios": { "bundleIdentifier": "moe.ppc.amnet", "supportsTablet": true, - "associatedDomains": [ - "applinks:card.ppc.moe" - ], "infoPlist": { "NSAppTransportSecurity": { "NSAllowsArbitraryLoads": true }, "UIDesignRequiresCompatibility": true, - "UIViewControllerBasedStatusBarAppearance": false, - "NFCReaderUsageDescription": "AMNet uses NFC to scan Physical IC Cards", - "com.apple.developer.nfc.readersession.felica.systemcodes": [ - "0003", - "88b4" - ] - }, - "entitlements": { - "com.apple.developer.nfc.readersession.formats": [ - "TAG" - ] + "UIViewControllerBasedStatusBarAppearance": false }, "config": { "usesNonExemptEncryption": false @@ -36,7 +23,7 @@ }, "android": { "package": "moe.ppc.amnet", - "versionCode": 8, + "versionCode": 9, "adaptiveIcon": { "foregroundImage": "./assets/icon-android.png", "backgroundColor": "#DED8FD" diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index 022e305..739e4a8 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -1,7 +1,6 @@ import {BSON} from "realm"; -import NfcManager from "react-native-nfc-manager"; +import {ActivityIndicator, Button, Text, useTheme} from "react-native-paper"; import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react"; -import {ActivityIndicator, Button, IconButton, Text, useTheme} from "react-native-paper"; import {Alert, Platform, RefreshControl, ScrollView, Share, StyleSheet, View} from "react-native"; import { CircleCheck, @@ -13,7 +12,6 @@ import { WifiOff, TvMinimalPlay, Star, - SmartphoneNfc, SquareArrowOutUpRight } from "lucide-react-native"; @@ -26,7 +24,6 @@ import RealmContext from "../models/RealmContext"; import {AMServer} from "../models/AMServer"; import {AMCard} from "../models/AMCard"; -import {felicaCardReadAsync, icCard0008Number, ICType} from "../lib/iccard"; import { AMCardInRequest, AMServerConnectionInfo, @@ -82,16 +79,6 @@ export default function ServerCardIn({route, navigation}) { } }, [realm, server, navigation]); - // nfc support - const [nfcSupported, setNfcSupported] = useState(Platform.select({android: false, default: null})); - useEffect(() => { - if (nfcSupported !== null) { - return; - } - - NfcManager.isSupported().then(setNfcSupported); - }, []); - // server state const [serverRefreshing, setServerRefreshing] = useState(false); const [serverReloadCounter, setServerReloadCounter] = useState(0); @@ -101,7 +88,6 @@ export default function ServerCardIn({route, navigation}) { const [cardInCompleted, setCardInCompleted] = useState(null); const disableCardIn = useMemo(() => cardInCompleted !== null || !serverConnectionSuccess, [cardInCompleted, serverConnectionSuccess]); - const serverDefaultCard = useMemo(() => { if (server?.isValid() !== true || !server.gameId) { return null; @@ -211,38 +197,6 @@ export default function ServerCardIn({route, navigation}) { return () => abortController.abort(); }, [server]); - const performPhysicalCardIn = useCallback(async () => { - const card = await felicaCardReadAsync(); - - if (!card) { - return; - } - - const savedCard = cards.find(x => x.physicalCardData?.cardIDm === card.idm); - - if (savedCard) { - setUserSelectedCard(savedCard); - cardInCallback(savedCard); - return; - } - - setCardInCompleted(false); - - const cardId = card.type !== ICType.Amusement ? icCard0008Number(card.idm) : card.accessCode; - const abortController = new AbortController(); - - performCardIn(server, {cardId, physicalCardIDm: card.idm}, abortController.signal).then(r => { - setCardInCompleted(r); - }).catch(() => { - setCardInCompleted(false); - }).finally(() => { - setTimeout(() => setCardInCompleted(null), 3000); - }); - - setTimeout(() => abortController.abort(), 5000); - return () => abortController.abort(); - }, [server]); - const performRefresh = () => { setServerRefreshing(true); setServerReloadCounter(serverReloadCounter + 1); @@ -262,8 +216,7 @@ export default function ServerCardIn({route, navigation}) { } return ( - }> + }> @@ -274,28 +227,16 @@ export default function ServerCardIn({route, navigation}) { actionIcon={}> {serverDefaultCard?.code == selectedCard.code && } - - - {nfcSupported === true && ( - }/> - )} - + icon={() => }> + {cardInCompleted === null ? "Card In" : cardInCompleted ? "Card In Success" : "Failed"} + : }/> @@ -329,8 +270,7 @@ export default function ServerCardIn({route, navigation}) { {serverDefaultCard?.code == card.code && } )) : - }/> + }/> } )} diff --git a/amnet-native/app/card/CardAddModal.tsx b/amnet-native/app/card/CardAddModal.tsx index ee0e482..a836e3b 100644 --- a/amnet-native/app/card/CardAddModal.tsx +++ b/amnet-native/app/card/CardAddModal.tsx @@ -1,15 +1,12 @@ +import React from "react"; import {StatusBar} from "expo-status-bar"; -import NfcManager from "react-native-nfc-manager"; - -import React, {useEffect, useState} from "react"; import {Platform, TouchableOpacity, View} from "react-native"; import {createNativeStackNavigator} from "@react-navigation/native-stack"; import {Card, Text, useTheme} from "react-native-paper"; -import {ShieldBan, SmartphoneNfc, SquareAsterisk, SquarePlus} from "lucide-react-native"; +import {SquareAsterisk, SquarePlus} from "lucide-react-native"; import ManualCardEntry from "./ManualCardEntry"; -import PhysicalCardScan from "./PhyscialCardScan"; const Stack = createNativeStackNavigator(); @@ -18,7 +15,6 @@ function CardAddModal() { - ) @@ -26,11 +22,6 @@ function CardAddModal() { function CardAddMethodSelector({navigation}) { const theme = useTheme(); - const [nfcSupported, setNfcSupported] = useState(null); - - useEffect(() => { - NfcManager.isSupported().then(setNfcSupported); - }, []); return ( <> @@ -45,19 +36,6 @@ function CardAddMethodSelector({navigation}) { - {nfcSupported !== null && ( - <> - : } - description={nfcSupported ? "Amusement IC or Transport cards supported" : "NFC is not supported on this device"} - onPress={() => navigation.navigate('Scan')}/> - - - - )} - } title="Enter Details Manually" @@ -106,8 +84,4 @@ function MethodCard(props: { ) } -// the nfc library doesn't offer the same support for felica cards on android compared to ios... -export default Platform.select({ - ios: CardAddModal, - default: ManualCardEntry -}); +export default CardAddModal; \ No newline at end of file diff --git a/amnet-native/app/card/PhyscialCardScan.tsx b/amnet-native/app/card/PhyscialCardScan.tsx deleted file mode 100644 index 3269c66..0000000 --- a/amnet-native/app/card/PhyscialCardScan.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import {StatusBar} from "expo-status-bar"; -import {useNavigation} from "@react-navigation/native"; -import {Card, Text, useTheme} from "react-native-paper"; -import {useCallback, useLayoutEffect, useState} from "react"; -import {Platform, TextInput, TouchableOpacity, View} from "react-native"; - -import RealmContext from "../../models/RealmContext"; -import {useModalStyles} from "../../utils/ModalStyles"; - -import {AMCard} from "../../models/AMCard"; -import {AMPhysicalCardData} from "../../models/AMPhysicalCardData"; -import {felicaCardReadAsync, icCard0008Number, ICType} from "../../lib/iccard"; - -const {useRealm} = RealmContext; - -export default function PhysicalCardScan() { - const realm = useRealm(); - const theme = useTheme(); - const navigation = useNavigation(); - const modalStyles = useModalStyles(); - - const [cardName, setCardName] = useState(''); - - const handleScan = useCallback(async () => { - const cardInfo = await felicaCardReadAsync(); - - if (!cardInfo) { - return; - } - - realm.write(() => { - // check if the card has already been registered (convert a manually entered card to a physically scanned card) - const accessCode = cardInfo.type === ICType.Amusement ? cardInfo.accessCode : icCard0008Number(cardInfo.idm); - const cardEntity = realm.objects(AMCard.schema.name).find(x => x.code === accessCode) ?? realm.create(AMCard.schema.name, AMCard.generate(accessCode, cardName)); - - // set physical card props - cardEntity.name = cardName; - cardEntity.added = new Date(); - cardEntity.physicalCardData ??= realm.objectForPrimaryKey(AMPhysicalCardData.schema.name, cardInfo.idm) ?? realm.create(AMPhysicalCardData.schema.name, { - cardIDm: cardInfo.idm, - cardType: cardInfo.type, - cardVendor: cardInfo.type === ICType.Amusement ? cardInfo.vendor : null - }); - }); - - // dismiss the parent screen (this screen can only be accessed from modal -> stack) - navigation.getParent()?.goBack(); - }, [realm, navigation, cardName]); - - useLayoutEffect(() => { - const canSave = cardName.length; - const themeOptions = canSave ? modalStyles.headerText : {color: theme.colors.outline}; - - navigation.setOptions({ - headerRight: () => handleScan()}> - Scan - - }) - }, [navigation, modalStyles, cardName]); - - return (<> - {Platform.OS == 'ios' && } - - - - - - Card Name - - - - - - - - Ensure you have either an Amusement IC or Transport card ready to scan. - After the scan has completed, the card will be saved to your device. - - - ) -} \ No newline at end of file diff --git a/amnet-native/lib/iccard-crypto.ts b/amnet-native/lib/iccard-crypto.ts deleted file mode 100644 index d474aec..0000000 --- a/amnet-native/lib/iccard-crypto.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {Buffer} from "buffer"; - -const s_box_inv = [ - "243cba36e385a4d0934373b9706ec9f1100e9b2c97e70b636c2920fe86f3e1f5f69fb616047b8fabb1392a1beb5ca8ac3811125f893e7dcaec53db6d1ed281789646fff9541c287a4fd3c0dcc16af2bccb57fd4ae4f0b2c79540625241e2ad49a6b51f02c8da92e5b0c564764821de0f45584cdfa784cfd5154e27806b7ffc44714722dd300ca13be037a03523903274bf8dc2ead650bbd9c483b43168558b5df47218b7eff7982d010361cc0a8e1300e814af095175a52f1d0d658acd66073d05d84be99d997c91d1b819c32b426988c679173a4d5ba25afb253fbdf8edcee687a326a92ebe94087e67608c9c5e6fb39e06fa82aeee5977d71a9a34d456aa33", - "f9817c00b93037d590516ef0b206fbcd39145ff81bc74a82708c921deac34ec812e6b610d32f95842542a572e58f55ef86a253aeed262047de786828e445cc35bc3ebb8ec0be34afa60964017b44f5f3b13fa4b33280c94f6f40bf214c74e1115eeb3d712e9d6275d41648771367ad6b5d0787f7a89a59768b9b1ed8eeaae9992dc597f1a783fccadcbab84be88917050ba06523d1ce3603d7e0f291df0e9c2c0d66d673585cb40a4dc23bd2b7e2ac33db6027bd56432404028d7d7ff6b5f4fddd5bec79c4221fa18854e71998947e31a329e35acb6ab0cf6d936c7a08a93c1cd06350c68538c14149ab61522a9fd9181a57462b69dafaff0f15963a8afe9e0c", - "11a29ac394a051015bf7d130ee1f06d57767d3c16ee76a1aa48e9f6566d60c2dc6e469b7565a5760fc791ee11652f007d0cdca78c98bb38827f6e0c084cf2fa304008040a7fa759d4b89462228373413ff20b4da08266c8ff25d7b735c4c9071418a9ebb12e8556d2a254aafbd9519a531baadd9c5a66b83c4b6a9ccf9a1b17adcc7db2e7e7f8d4e620f0339fdb8d418c2ec6102531c3b7dbc1d59f58c98f16f9385f82c74d85e4d97ab87820a21425f0d5833443adde9fb5047c8d7819be51b1754862beff429320e7c2315ebe63c35e296683f0b43cede9cbe4f457276b510e3df92d2a84891b9acbf49b2996470eaf33e6314aeedaa24fe3dcbb009383605", - "c4f02849554af5fd75af2069c843866bc9a8c6544cdd025be89b597734d7c0515ff37ed4f19081ce198a7833cd978cd66a4b8fa480dc1a1714c5078766ad9d85b2f88d98df3e1f3f645840ac6c5c085d53baffd2a92c25abe432389af9cbc29167d0e322292d9248b40e9905a36f0a15efd5104fd8f600e6462b31578394ae03eb8e1324a11e5ed126d9a7ca366e7137eeb1fa7c7606b823be21bc9ec1da7a3b62277bb0e56318827f0feda02a9cbf11bde0880d3a795256f7b6d309161b70b342602f1ca29f72124547bbe10b018b1ddeec0c4ee9cfcc9574f4a593c7db4dfb3565fee739b5ea96c304414484fc6d30e2f268b7897d733db95a50a63c61aa2e", - "1a599cadc8e41154ed370f3ae65f3c4bb81589b1e8da697791568bdb0624cf18f8b087df8c35cb86539da4664a7a710a4838ffdc8320ce9832f6d7af705e738a14721ec129790708e94346d02fde2a4f3d2c50b375fc0b64ae317b61a530a093d2bec255ba6cf36268fdac3b95491f6bb485f7a603ec6e9a81090c6aee9e4ef0ab2d7ea1e1bbc9bc41f2fa2e1bdd2734d36004b501d640f9d50247d9d8e08f2bfbb7c5eb5716e5788df400cd8239c696bdbfe336450e261d63e7843eb69b22a3f58e236dc3997d9097102580d44ce274ccb25c33c7ea05123f51a8fef11c42cad176286f92a767a919a2445baaef587f21c08865b95d4d0d5ac413527c9f9417", - "c72b82615bd09684d34a70a19b59339fc02014532917c50bc97b97020d3a1e7c3f6b52e8753df6e40c8a4fc35f2665733123284874aaa73609b1e291045122fc08a605a4f1121c19eb4037c2a0411dd4dc07438f47afd12e98ab01baf06668acf9e769b6cb8d78871503d5dfa31a9d6aea2f944e9e42d7b83892d8bbdedd9abcb04c79f4583ee98381ffe355fd5db2ef9c6d549960da3becfa11d6c42aed4bae13bfb9068be01f7f5aad90390ff3bd466c2cf2f8fed9e6720e89be5ec6a8cff5577e8cb7e1887ac81b18db6f35d216326463a58ef7b47695867de5a95c8500c1934dfb30a2b5253410773c67712d4445560a50b3cd6280ce214924caee276ecc", - "a5b06db251551fbf3e8ddd1992ea1bbd326529a48967b1906800da0da65954f210142f45e03b23fad750ac93e643010a5c78709846a97bf395072ad374b33fa36082394e3448c01cf6c291644d3cf89d359a94e37af0f96eb912b8042d02281385728087db2bf14f26a2e1497e9ccca7b6a0d09b3677ad8b6b4a031d058a064bafe531b5d4c60c66ba83fd090fae71b76fdc41e8178e407f6230ffa88425fb16ce3744ab991eeb183a47f75f81cfed582c6cfe9e57539720ca791a5a88f5699fe7d90ebe42df56e44c22aa730b15c5eefcc7d6cbcd8ce27621e9d1ecc87dd88f617c2ebcdeb475d2c4633da15e5d6a0824c927bbef33865bd5385211f4c396c1", - "345adb2c5957122b30c2a092bfedbc45de279b96d3e6c5ebd8244ba421cca8d6ce3cbab109e0d732664a831d19ca89670f420771e9bb445e8517c4ae9c3f6b78e76d02a7fe33e3d90a7deaf118877b6203795223003e25b9dd16683b1f90a62ac629918a9def1e8476ee4f39fb111b0b93ad49b705e14dcbe838d055f2f70d8b65cdfad49ec9814e14c826b6f9aaf580f86a98ab58f3b88eb5974372a2da64c1405c13b0e5bd089f1c7e8f06be6e501a2f37a1fc102e705304206348e2fd6f7a7561b3353acf5b888251d247a5a9746c31947c9ac0ec150e28012dc3f0b4e48ddcac3d5f86c79522f4b2a35446d1778c0cffaf565d36697f994cd56073df41f6", - "04da79631ebdeae30b652501cda992ed187557308903096adcc798a450911fb10c778566ca121c67c2e01783599dd5228256a89fc66048b311fca328fb06dd5dba297a4fe8fee910cbf3937b6c6954e744a2841d8dcefffa1a879074a1f814aabcc0cf31b4d9bfd85e262dd0e43f19d68c2fab3958726edf3be63cb66288de40b28f9b7c9543d19eac9cd423e10e4b5333462013341b97f7f1c3614a6f5a217f702e554105c5d776e52715ec425c4d78358befd2eeb5beae023ad35fc424f0f9514eeb000ffdaf3ec19a528681807ef62bccb97d68f2ad99a7072c7338b06bb78e71a0f43da60d37db0a473616966d322a5be24594f5a54cc88a4964bb08c9b8" -]; - -function rotate_right(data: Uint8Array, nBytes: number, nBits: number) { - let prior = data[nBytes - 1]; - - for (let i = 0; i < nBytes; i++) { - let current = data[i]; - - data[i] = (current >> nBits) | ((prior & ((1 << nBits) - 1)) << (8 - nBits)); - prior = current; - } -} - -const S_BOX_INV = s_box_inv.map(x => new Uint8Array(Buffer.from(x, 'hex'))); -const N_TABLES = S_BOX_INV.length - 1; -const ITER_ADD = 5; - -export function performDecrypt(data: Uint8Array) { - const spad = data.map(x => S_BOX_INV[N_TABLES][x]); - - let count = (spad[15] >> 4) + 7; - let table = spad[15] + ITER_ADD * count; - - for (let z = 0; z < count; z++) { - table -= ITER_ADD; - rotate_right(spad, 15, 5); - - for (let i = 0; i < 15; i++) { - spad[i] = S_BOX_INV[table % N_TABLES][spad[i]]; - } - } - - return { - version: spad[0], - accessCodeBytes: spad.slice(6) // access code is a binary-coded decimal - }; -} diff --git a/amnet-native/lib/iccard.ts b/amnet-native/lib/iccard.ts index 9404b69..d56ea2e 100644 --- a/amnet-native/lib/iccard.ts +++ b/amnet-native/lib/iccard.ts @@ -1,8 +1,3 @@ -import {Buffer} from "buffer"; -import NfcManager, {NfcTech} from "react-native-nfc-manager"; - -import {performDecrypt} from "./iccard-crypto"; - export enum ICType { Amusement = 1, Transport = 2 @@ -41,92 +36,3 @@ export function icCard0008Number(idm: string): string { // 0008 is the numerical representation of the idm padded with zeros until it is 20 digits return BigInt(`0x${idm}`).toString().padStart(20, '0'); } - -/** - * Reads a FeliCa card, returning access code information. - */ -export async function felicaCardReadAsync(): Promise { - try { - await NfcManager.requestTechnology(NfcTech.FelicaIOS, { - isReaderModeEnabled: true, - invalidateAfterFirstRead: false, - alertMessage: "Hold an Amusement IC or Transport card to the top of your phone to scan it." - }); - - const tag = await NfcManager.getTag(); - const idm = tag['idm'] as string; - - switch (tag['systemCode'] as string) { - case "0003": - return { - type: ICType.Transport, - idm - }; - - case "88B4": - return await readAmusementICCardContents(idm); - - default: - return null; - } - } catch (ex) { - return null; - } finally { - await NfcManager.cancelTechnologyRequest(); - } -} - -async function readAmusementICCardContents(idm: string): Promise { - const idmBytes = Uint8Array.from(Buffer.from(idm, 'hex')); - const readCommand = [ - 0x06, // read - ...idmBytes, - 0x01, // service count (fixed) - 0x0B, 0x00, // service code (000b, le) - 0x02, // block count - 0x80, 0x00, // S_PAD0 block - 0x80, 0x82 // ID block - ]; - - let response = await NfcManager.sendFelicaCommandIOS(readCommand); - const offset = response.indexOf(0x07); // look for 0x07 as for some reason the response may not start at response[0] - - // response must have 44 bytes - if (offset === -1 || response.length - offset !== 44) { - return null; - } else if (offset !== 0) { - response = response.slice(offset); - } - - // response format: - // 0 - read response (0x07) - // 1-8 - IDm - // 9 - status flag 1 - // 10 - status flag 2 - // 11 - number of blocks (0x02) - // 12-27 - S_PAD0 block - // 28-43 - ID block (DFC is 35-36) - - if (response[9] !== 0x00 || response[11] !== 0x02) { - return null; - } - - const dfcValue = response[37] | (response[36] << 8); - - if (dfcValue in AmusementICVendor && dfcValue !== AmusementICVendor.Unknown) { - const accessCode = performDecrypt(new Uint8Array(response.slice(12, 28))); - return { - type: ICType.Amusement, - accessCode: [...accessCode.accessCodeBytes].map(x => x.toString(16).padStart(2, '0')).join(''), - vendor: dfcValue, - idm - }; - } - - return { - type: ICType.Amusement, - accessCode: icCard0008Number(idm), - vendor: AmusementICVendor.Unknown, - idm - }; -} diff --git a/amnet-native/package-lock.json b/amnet-native/package-lock.json index b41741f..7ae0bac 100644 --- a/amnet-native/package-lock.json +++ b/amnet-native/package-lock.json @@ -1,12 +1,12 @@ { "name": "amnet-native", - "version": "1.3.2", + "version": "1.3.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "amnet-native", - "version": "1.3.2", + "version": "1.3.9", "dependencies": { "@babel/runtime": "^7.28.4", "@expo-google-fonts/jetbrains-mono": "^0.4.1", @@ -32,7 +32,6 @@ "react-native-context-menu-view": "^1.21.0", "react-native-gesture-handler": "^2.30.0", "react-native-get-random-values": "^2.0.0", - "react-native-nfc-manager": "^3.17.2", "react-native-paper": "^5.14.5", "react-native-reanimated": "~4.2.1", "react-native-safe-area-context": "5.6.2", @@ -1626,7 +1625,6 @@ "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.4.tgz", "integrity": "sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==", "license": "MIT", - "peer": true, "dependencies": { "@expo/config-types": "^54.0.10", "@expo/json-file": "~10.0.8", @@ -7344,20 +7342,6 @@ "react-native": "*" } }, - "node_modules/react-native-nfc-manager": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/react-native-nfc-manager/-/react-native-nfc-manager-3.17.2.tgz", - "integrity": "sha512-0NryP/Iw2hzw4MVH5KCngoRerNUrnRok6VfLrlFcFZRKyTQ7KTgpsdDxCB6cR33qYNyEDrWGBayfAI+ym5gt8Q==", - "license": "MIT", - "peerDependencies": { - "@expo/config-plugins": "*" - }, - "peerDependenciesMeta": { - "@expo/config-plugins": { - "optional": true - } - } - }, "node_modules/react-native-paper": { "version": "5.14.5", "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.14.5.tgz", @@ -8519,9 +8503,9 @@ } }, "node_modules/tar": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -8810,9 +8794,9 @@ } }, "node_modules/undici": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz", - "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", "license": "MIT", "engines": { "node": ">=18.17" diff --git a/amnet-native/package.json b/amnet-native/package.json index bbfd3da..392d9cf 100644 --- a/amnet-native/package.json +++ b/amnet-native/package.json @@ -1,6 +1,6 @@ { "name": "amnet-native", - "version": "1.3.2", + "version": "1.3.9", "private": true, "scripts": { "android": "expo run:android", @@ -33,7 +33,6 @@ "react-native-context-menu-view": "^1.21.0", "react-native-gesture-handler": "^2.30.0", "react-native-get-random-values": "^2.0.0", - "react-native-nfc-manager": "^3.17.2", "react-native-paper": "^5.14.5", "react-native-reanimated": "~4.2.1", "react-native-safe-area-context": "5.6.2", diff --git a/amnet-shared/package-lock.json b/amnet-shared/package-lock.json index ebda06e..9221e3f 100644 --- a/amnet-shared/package-lock.json +++ b/amnet-shared/package-lock.json @@ -1253,9 +1253,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, "node_modules/lru-cache": { diff --git a/readme.md b/readme.md index 4080b10..fa1a59c 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@
# AMNet -[TestFlight](https://testflight.apple.com/join/kHbeJTsQ) • [Firebase](https://appdistribution.firebase.dev/i/6acc3151357deaa8) • [Website](http://card.ppc.moe) • [Server DLLs](https://gitea.tendokyu.moe/ppc/amnet/releases) +[Firebase](https://appdistribution.firebase.dev/i/6acc3151357deaa8) • [Website](http://card.ppc.moe) • [Server DLLs](https://gitea.tendokyu.moe/ppc/amnet/releases) An Amusement IC Card Switcher for segatools-based games. @@ -11,6 +11,9 @@ An Amusement IC Card Switcher for segatools-based games. AMNet is designed to acts as dynamic version of the `DEVICE/aime.txt` file, allowing multiple users to log into a game using their own mobile devices. This was originally intended for use with maimai and CHUNITHM, but should be compatible with any segatools-based game with that uses Amusement IC for account management. +> [!WARNING] +> TestFlight is no longer available and must be sideloaded using AltStore or similar tooling for the forseeable future. + ## Setup 1. Download the latest `amnet.dll` from the releases page 2. Move `amnet.dll` to the game directory @@ -26,7 +29,7 @@ This was originally intended for use with maimai and CHUNITHM, but should be com ; serverName - the name of the server to show to users (defaults to the computer name if not set) ; serverAddress - the address to listen on, including the protocol (don't set/change unless you know what you're doing) ; enableKeyboardMode - whether to enable hold-enter-to-login mode. Reads the file set at aimePath for card number (or DEVICE/aime.txt if not set). - ; useAimeDBForPhysicalCards - Whether physical cards should use AiMeDB to retrieve access codes. If the game is using a hosted network, set this to 1 to load the same account data/profile as you would get on a physical cab. + ; useAimeDBForPhysicalCards - Whether codes from real cards should use AiMeDB to retrieve access codes. If the game is using a hosted network, set this to 1 to load the same account data/profile as you would get on a physical cab. gameId=SDEZ ;serverName=CHUNI-PENGUIN ;serverAddress=http://0.0.0.0:6070 @@ -40,13 +43,13 @@ This was originally intended for use with maimai and CHUNITHM, but should be com AMNet 1.3 adds support for scanning physical cards on iOS devices. If you own a physical Aime/Banapass/NESiCA/e-Amusement card, you can scan it using the app and use it in-game. By changing the `useAimeDBForPhysicalCards` in segatools.ini, you can choose how the card is read on a per-game basis: -- `0` will only use the 20-digit access code (as shown in the app) to load the user profile directly (default option). -- `1` will present the card's hardware identifier (IDm) to the game and the AiMeDB server configures will be used to retrieve the 20-digit code assigned to the card. +- `0` will send the 20-digit access code on the back of the card to the game and load the save data directly (default option). +- `1` - Send the card's IDm to the game and use AiMeDB to retrieve the 20-digit code assigned to the card. Mode `1` makes the app function in the same way to a real card reader and should be used if you want the same access code to show when scanning the card on a cab. You should also use this option if you're connecting to a hosted server (AquaNet/rinnet/etc). -Note: if `useAimeDBForPhysicalCards` is set to `1` and the game is failing to read the card, make sure an up-to-date version of segatools is being used. +Additionally, if `useAimeDBForPhysicalCards` is set to `1` and the game is failing to read the card, make sure an up-to-date version of segatools is being used. ## Interfaces AMNet can be used on most platforms, see below for options available for each platform: