mirror of
https://gitea.tendokyu.moe/eamuse/docs.git
synced 2024-11-23 22:40:57 +01:00
Card ID docs
This commit is contained in:
parent
c8e7fc5d86
commit
20affcfa63
488
cardid.html
Normal file
488
cardid.html
Normal file
@ -0,0 +1,488 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>e-Amusement API</title>
|
||||
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href=".">Contents</a></td>
|
||||
<td><a href="transport.html">Transport layer</a></td>
|
||||
<td><a href="packet.html">Packet format</a></td>
|
||||
<td><a href="protocol.html">Application Protocol</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h1>Card ID generation</h1>
|
||||
<details>
|
||||
<summary>I'm just here for code.</summary>
|
||||
<p>Fair. My intent with these pages is to describe things in enough detail that they should be simple to
|
||||
implement yourself, but this is one of those things that's quite easy to just drop in some pre-made code
|
||||
for. My local implementation is in python, so that's all you're getting :). As a free bonus, have some test
|
||||
cases too. It's not great code by any stretch, and it liberally uses assertions rather than proper
|
||||
exceptions, but it should be a good enough starting point for your own version.</p>
|
||||
<pre><code>import binascii
|
||||
from Crypto.Cipher import DES3
|
||||
|
||||
|
||||
KEY = b"" # Check the <a href="#des">DES section</a> for this
|
||||
_KEY = bytes(i * 2 for i in KEY) # Preprocess the key
|
||||
|
||||
ALPHABET = "0123456789ABCDEFGHJKLMNPRSTUWXYZ"
|
||||
|
||||
|
||||
def enc_des(uid):
|
||||
cipher = DES3.new(_KEY, DES3.MODE_CBC, iv=b'\0' * 8)
|
||||
return cipher.encrypt(uid)
|
||||
|
||||
|
||||
def dec_des(uid):
|
||||
cipher = DES3.new(_KEY, DES3.MODE_CBC, iv=b'\0' * 8)
|
||||
return cipher.decrypt(uid)
|
||||
|
||||
|
||||
def checksum(data):
|
||||
chk = sum(data[i] * (i % 3 + 1) for i in range(15))
|
||||
|
||||
while chk > 31:
|
||||
chk = (chk >> 5) + (chk & 31)
|
||||
|
||||
return chk
|
||||
|
||||
|
||||
def pack_5(data):
|
||||
data = "".join(f"{i:05b}" for i in data)
|
||||
if len(data) % 8 != 0:
|
||||
data += "0" * (8 - (len(data) % 8))
|
||||
return bytes(int(data[i:i+8], 2) for i in range(0, len(data), 8))
|
||||
|
||||
|
||||
def unpack_5(data):
|
||||
data = "".join(f"{i:08b}" for i in data)
|
||||
if len(data) % 5 != 0:
|
||||
data += "0" * (5 - (len(data) % 5))
|
||||
return bytes(int(data[i:i+5], 2) for i in range(0, len(data), 5))
|
||||
|
||||
|
||||
def to_konami_id(uid):
|
||||
assert len(uid) == 16, "UID must be 16 bytes"
|
||||
|
||||
if uid.upper().startswith("E004"):
|
||||
card_type = 1
|
||||
elif uid.upper().startswith("0"):
|
||||
card_type = 2
|
||||
else:
|
||||
raise ValueError("Invalid UID prefix")
|
||||
|
||||
kid = binascii.unhexlify(uid)
|
||||
assert len(kid) == 8, "ID must be 8 bytes"
|
||||
|
||||
out = bytearray(unpack_5(enc_des(kid[::-1]))[:13]) + b'\0\0\0'
|
||||
|
||||
out[0] ^= card_type
|
||||
out[13] = 1
|
||||
for i in range(1, 14):
|
||||
out[i] ^= out[i - 1]
|
||||
out[14] = card_type
|
||||
out[15] = checksum(out)
|
||||
|
||||
return "".join(ALPHABET[i] for i in out)
|
||||
|
||||
|
||||
def to_uid(konami_id):
|
||||
if konami_id[14] == "1":
|
||||
card_type = 1
|
||||
elif konami_id[14] == "2":
|
||||
card_type = 2
|
||||
else:
|
||||
raise ValueError("Invalid ID")
|
||||
|
||||
assert len(konami_id) == 16, f"ID must be 16 characters"
|
||||
assert all(i in ALPHABET for i in konami_id), "ID contains invalid characters"
|
||||
card = [ALPHABET.index(i) for i in konami_id]
|
||||
assert card[11] % 2 == card[12] % 2, "Parity check failed"
|
||||
assert card[13] == card[12] ^ 1, "Card invalid"
|
||||
assert card[15] == checksum(card), "Checksum failed"
|
||||
|
||||
for i in range(13, 0, -1):
|
||||
card[i] ^= card[i - 1]
|
||||
|
||||
card[0] ^= card_type
|
||||
|
||||
card_id = dec_des(pack_5(card[:13])[:8])[::-1]
|
||||
card_id = binascii.hexlify(card_id).decode().upper()
|
||||
|
||||
if card_type == 1:
|
||||
assert card_id[:4] == "E004", "Invalid card type"
|
||||
elif card_type == 2:
|
||||
assert card_id[0] == "0", "Invalid card type"
|
||||
return card_id
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
assert to_konami_id("0000000000000000") == "007TUT8XJNSSPN2P", "To KID failed"
|
||||
assert to_uid("007TUT8XJNSSPN2P") == "0000000000000000", "From KID failed"
|
||||
assert to_uid(to_konami_id("000000100200F000")) == "000000100200F000", "Roundtrip failed"
|
||||
</code></pre>
|
||||
</details>
|
||||
<p>e-Amusement cards use 16 digit IDs. KONAMI IDs are also 16 digits. Are they related? Yes! In fact, KONAMI IDs are
|
||||
derived from the ID stored on the e-Amusement card.</p>
|
||||
<p>KONAMI IDs have an alphabet of <code>0123456789ABCDEFGHJKLMNPRSTUWXYZ</code> (note that <code>IOQV</code> are
|
||||
absent), whereas e-A IDs (yeah I'm not typing that out every time) have an alphabet of
|
||||
<code>0123456789ABCDEF</code> (hex). It stands to reason then that there's additional information present in
|
||||
KONAMI IDs, as they are the same length, but can hold a greater density of information. That intuition would be
|
||||
correct.
|
||||
</p>
|
||||
<h2 id="konami">Converting KONAMI IDs to e-Amusement IDs</h2>
|
||||
<p>Let's take a look at the format of KONAMI IDs. The first step before we can do anything is to convert it from a
|
||||
string to a series of integers. Each byte is replaced with its index in the alphabet, giving us 16 values
|
||||
ranging from 0 through 31. These bytes has the following meanings:</p>
|
||||
<table class="code">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
<td>5</td>
|
||||
<td>6</td>
|
||||
<td>7</td>
|
||||
<td>8</td>
|
||||
<td>9</td>
|
||||
<td>10</td>
|
||||
<td>11</td>
|
||||
<td>12</td>
|
||||
<td>13</td>
|
||||
<td>14</td>
|
||||
<td>15</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td colspan="13">e-Amusement ID</td>
|
||||
<td>Check byte</td>
|
||||
<td>Card type</td>
|
||||
<td>Checksum</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>Due to how IDs are constructed, there are a number of checks we can perform to validate an ID:</p>
|
||||
<ul>
|
||||
<li>Parity check: <code>[11] % 2 == [12] % 2</code></li>
|
||||
<li>Encoding check: <code>[13] == [12] ^ 1</code></li>
|
||||
<li>Checksum: <code>[15] == <a href="#checksum">checksum([0..14])</a></code></li>
|
||||
<li>Post-decoding, FeliCa cards start with <code>0</code> and magnetic strip cards start with <code>E004</code>.
|
||||
</li>
|
||||
<li>Card type: <code>[14] == 1</code> (magnetic stripe) or <code>[14] == 2</code> (FeliCa)</li>
|
||||
</ul>
|
||||
|
||||
<p>To decrypt a KONAMI ID, at a high level we must:</p>
|
||||
<ul>
|
||||
<li>Remove the XOR encoding</li>
|
||||
<li>5-pack the ID</li>
|
||||
<li>Decrypt the packed ID</li>
|
||||
<li>Reverse the bytes</li>
|
||||
<li>Convert to upper-case hex</li>
|
||||
</ul>
|
||||
<p>As we'll see in the next section, card IDs have an XOR pass performed. This is what allows for the above encoding
|
||||
check, but we must remove it before we can begin decoding. A line of code speaks a thousand words, so have
|
||||
three:</p>
|
||||
<pre><code>for i from 13 to 1 inclusive:
|
||||
card[i] ^= card[i - 1]
|
||||
card[0] ^= card_type</code></pre>
|
||||
<p id="packing">The values in the <code>e-Amusement ID</code> field above will all maximally be 31
|
||||
(<code>0b11111</code>) therefore before we perform the decryption step we first densly pack these 5-bit
|
||||
integers. That is, <code>0b11111 0b00000 0b11111</code> would be packed to <code>0b11111000 0b00111110</code>.
|
||||
This value will be 8 bytes long. We can now <a href="#des">decrypt it</a>, reverse it, and convert to hex.
|
||||
</p>
|
||||
<details>
|
||||
<summary>Implementing 5-bit packing</summary>
|
||||
<p>In most languages, implementing a packer can be done one of two ways. The approach chosen by Bemani is to
|
||||
first create a table of every individual bit (each stored in a whole byte!), then iterate through the bits
|
||||
ORing them together. This is a simple but wasteful implementation. The other approach is to use a buffer
|
||||
byte and slowly shift values in, tracking how many bits are stored in the buffer byte, and performing
|
||||
different actions depending on how many bits this is. For both packing and unpacking this requires three
|
||||
cases. It's somewhat more complex, but less wasteful in terms of memory usage. Pick your poison I suppose.
|
||||
</p>
|
||||
<details>
|
||||
<summary>In <i>most</i> languages?</summary>
|
||||
<p>Haha well you see we can actually cheat and use string manipulation. Wasteful? Incredibly. Efficient? Not
|
||||
at all. Quick and easy? Yup!</p>
|
||||
<pre><code>def pack_5(data):
|
||||
data = "".join(f"{i:05b}" for i in data)
|
||||
if len(data) % 8 != 0:
|
||||
data += "0" * (8 - (len(data) % 8))
|
||||
return bytes(int(data[i:i+8], 2) for i in range(0, len(data), 8))
|
||||
|
||||
def unpack_5(data):
|
||||
data = "".join(f"{i:08b}" for i in data)
|
||||
if len(data) % 5 != 0:
|
||||
data += "0" * (5 - (len(data) % 5))
|
||||
return bytes(int(data[i:i+5], 2) for i in range(0, len(data), 5))</code></pre>
|
||||
<p>If your language of choice allows this, and you don't care for efficiency, this can be a great time-saver
|
||||
towards get something working. Truth be told my local implementation originally used the Bemani method
|
||||
(it was a line-for-line port, after all), switched to the second method, then I opted for this hacky
|
||||
string method in the name of code clarity.</p>
|
||||
</details>
|
||||
</details>
|
||||
|
||||
<h2 id="eaid">Converting e-Amusement IDs to KONAMI IDs</h2>
|
||||
<p>This is mostly the above process, in reverse, but we need to make sure to populate some of the extra check bytes.
|
||||
</p>
|
||||
<p>Before we start, we need to make sure we have a valid card! FeliCa cards (type <code>2</code>) will begin with a
|
||||
single null nibble, and magnetic stripe cards (type <code>1</code>) with the word <code>E004</code>. We then
|
||||
parse the entire ID as a hex string, giving us an 8-byte value.</p>
|
||||
<p>This value is reversed, and <a href="#des">encrypted</a>. After encryption, we need to unpack it from it's
|
||||
5-packed format. This is the same process as <a href="#packing">unpacking</a>, but reversed. The unpacked data
|
||||
can be ambiguous in length. It's 13 bytes. If your unpacker produces a 14th byte, it'll be null and can be
|
||||
discarded.</p>
|
||||
<p>We pad the 13 bytes with 3 extra null bytes, then apply our checks to the ID:</p>
|
||||
<pre><code>card[0] ^= card_type
|
||||
card[13] = 1
|
||||
for i from 0 to 13 inclusive:
|
||||
card[i + 1] ^= card[i]
|
||||
card[14] = card_type
|
||||
card[15] = <a href="#checksum">checksum(card)</a></code></pre>
|
||||
|
||||
<p>This leaves us with 16 values ranging from 0 to 31, which we apply as indecies to our alphabet to produce the
|
||||
final ID.</p>
|
||||
|
||||
<h2 id="checksum">Checksums</h2>
|
||||
<p>As if the encryption and XOR wasn't enough, card IDs also contain a checksum to make <i>absolutely</i> sure the
|
||||
card is valid. I could explain in words how the checksum works, but that's probably not very useful. Have a
|
||||
pseudocode snippet instead:</p>
|
||||
<pre><code>checksum(bytes):
|
||||
chk = 0
|
||||
for i from 0 to 14 inclusive:
|
||||
chk += bytes[i] * (i % 3 + 1)
|
||||
|
||||
while chk > 31:
|
||||
chk = (chk >> 5) + (chk & 31)
|
||||
|
||||
return chk</code></pre>
|
||||
|
||||
<h2 id="des">The DES scheme used</h2>
|
||||
<p>For whatever reason, Bemani decided that IDs should be encrypted. Thankfully however they used triple DES, which
|
||||
almost certainly has an existing implementation in your language of choice. It is triple DES, in CBC mode, with
|
||||
a totally null <code>IV</code>. The key is quite easy to find if you hit the right binaries with
|
||||
<code>strings</code>. <span style="color: white">Alternatively, check the source of this page.</span> The key
|
||||
contains characters that are all within the ASCII range. Before we can use it with DES, the value of every byte
|
||||
needs doubled. This was presumably done to give the values more range, but I sincerely doubt it adds any
|
||||
additional security.
|
||||
</p>
|
||||
<!-- Oh hello there. "?I'llB2c.YouXXXeMeHaYpy!" (without the quotes). Don't forget to double every byte before using
|
||||
the key, giving us an actual key of 7e924ed8d88464c65cb2deeab0b0b0ca9aca90c2b2e0f242. -->
|
||||
<details>
|
||||
<summary>I'm curious how Bemani implemented this in their own code!</summary>
|
||||
<p>Curiosity is a great thing. Unfortunately, this is code that is implement within the game specific DLL files.
|
||||
If you happen to have SDXV 4 in front of you too, head over over to <code>soundvoltex.dll:0x1027316f</code>
|
||||
and you should see everything you need.
|
||||
</p>
|
||||
<p>As part of breaking down how this all works, I produced a more or less line-for-line Python port of the game
|
||||
code, for testing, validation, etc.. It's not especially pretty, but should give you an idea of how it works
|
||||
under the hood. One interesting observation is that it looks like the initial and final permutation steps
|
||||
were inlined. It's also possible that they did the whole thing with macros rather than inline functions.
|
||||
Either way, my python port didn't do any cleaning up, because we can just use a DES library.</p>
|
||||
<details>
|
||||
<summary>Show me that!</summary>
|
||||
<pre><code>DES_KEYMAP = [
|
||||
[0x02080008, 0x02082000, 0x00002008, 0x00000000, 0x02002000, 0x00080008, 0x02080000, 0x02082008, 0x00000008, 0x02000000, 0x00082000, 0x00002008, 0x00082008, 0x02002008, 0x02000008, 0x02080000, 0x00002000, 0x00082008, 0x00080008, 0x02002000, 0x02082008, 0x02000008, 0x00000000, 0x00082000, 0x02000000, 0x00080000, 0x02002008, 0x02080008, 0x00080000, 0x00002000, 0x02082000, 0x00000008, 0x00080000, 0x00002000, 0x02000008, 0x02082008, 0x00002008, 0x02000000, 0x00000000, 0x00082000, 0x02080008, 0x02002008, 0x02002000, 0x00080008, 0x02082000, 0x00000008, 0x00080008, 0x02002000, 0x02082008, 0x00080000, 0x02080000, 0x02000008, 0x00082000, 0x00002008, 0x02002008, 0x02080000, 0x00000008, 0x02082000, 0x00082008, 0x00000000, 0x02000000, 0x02080008, 0x00002000, 0x00082008],
|
||||
[0x08000004, 0x00020004, 0x00000000, 0x08020200, 0x00020004, 0x00000200, 0x08000204, 0x00020000, 0x00000204, 0x08020204, 0x00020200, 0x08000000, 0x08000200, 0x08000004, 0x08020000, 0x00020204, 0x00020000, 0x08000204, 0x08020004, 0x00000000, 0x00000200, 0x00000004, 0x08020200, 0x08020004, 0x08020204, 0x08020000, 0x08000000, 0x00000204, 0x00000004, 0x00020200, 0x00020204, 0x08000200, 0x00000204, 0x08000000, 0x08000200, 0x00020204, 0x08020200, 0x00020004, 0x00000000, 0x08000200, 0x08000000, 0x00000200, 0x08020004, 0x00020000, 0x00020004, 0x08020204, 0x00020200, 0x00000004, 0x08020204, 0x00020200, 0x00020000, 0x08000204, 0x08000004, 0x08020000, 0x00020204, 0x00000000, 0x00000200, 0x08000004, 0x08000204, 0x08020200, 0x08020000, 0x00000204, 0x00000004, 0x08020004],
|
||||
[0x80040100, 0x01000100, 0x80000000, 0x81040100, 0x00000000, 0x01040000, 0x81000100, 0x80040000, 0x01040100, 0x81000000, 0x01000000, 0x80000100, 0x81000000, 0x80040100, 0x00040000, 0x01000000, 0x81040000, 0x00040100, 0x00000100, 0x80000000, 0x00040100, 0x81000100, 0x01040000, 0x00000100, 0x80000100, 0x00000000, 0x80040000, 0x01040100, 0x01000100, 0x81040000, 0x81040100, 0x00040000, 0x81040000, 0x80000100, 0x00040000, 0x81000000, 0x00040100, 0x01000100, 0x80000000, 0x01040000, 0x81000100, 0x00000000, 0x00000100, 0x80040000, 0x00000000, 0x81040000, 0x01040100, 0x00000100, 0x01000000, 0x81040100, 0x80040100, 0x00040000, 0x81040100, 0x80000000, 0x01000100, 0x80040100, 0x80040000, 0x00040100, 0x01040000, 0x81000100, 0x80000100, 0x01000000, 0x81000000, 0x01040100],
|
||||
[0x04010801, 0x00000000, 0x00010800, 0x04010000, 0x04000001, 0x00000801, 0x04000800, 0x00010800, 0x00000800, 0x04010001, 0x00000001, 0x04000800, 0x00010001, 0x04010800, 0x04010000, 0x00000001, 0x00010000, 0x04000801, 0x04010001, 0x00000800, 0x00010801, 0x04000000, 0x00000000, 0x00010001, 0x04000801, 0x00010801, 0x04010800, 0x04000001, 0x04000000, 0x00010000, 0x00000801, 0x04010801, 0x00010001, 0x04010800, 0x04000800, 0x00010801, 0x04010801, 0x00010001, 0x04000001, 0x00000000, 0x04000000, 0x00000801, 0x00010000, 0x04010001, 0x00000800, 0x04000000, 0x00010801, 0x04000801, 0x04010800, 0x00000800, 0x00000000, 0x04000001, 0x00000001, 0x04010801, 0x00010800, 0x04010000, 0x04010001, 0x00010000, 0x00000801, 0x04000800, 0x04000801, 0x00000001, 0x04010000, 0x00010800],
|
||||
[0x00000400, 0x00000020, 0x00100020, 0x40100000, 0x40100420, 0x40000400, 0x00000420, 0x00000000, 0x00100000, 0x40100020, 0x40000020, 0x00100400, 0x40000000, 0x00100420, 0x00100400, 0x40000020, 0x40100020, 0x00000400, 0x40000400, 0x40100420, 0x00000000, 0x00100020, 0x40100000, 0x00000420, 0x40100400, 0x40000420, 0x00100420, 0x40000000, 0x40000420, 0x40100400, 0x00000020, 0x00100000, 0x40000420, 0x00100400, 0x40100400, 0x40000020, 0x00000400, 0x00000020, 0x00100000, 0x40100400, 0x40100020, 0x40000420, 0x00000420, 0x00000000, 0x00000020, 0x40100000, 0x40000000, 0x00100020, 0x00000000, 0x40100020, 0x00100020, 0x00000420, 0x40000020, 0x00000400, 0x40100420, 0x00100000, 0x00100420, 0x40000000, 0x40000400, 0x40100420, 0x40100000, 0x00100420, 0x00100400, 0x40000400],
|
||||
[0x00800000, 0x00001000, 0x00000040, 0x00801042, 0x00801002, 0x00800040, 0x00001042, 0x00801000, 0x00001000, 0x00000002, 0x00800002, 0x00001040, 0x00800042, 0x00801002, 0x00801040, 0x00000000, 0x00001040, 0x00800000, 0x00001002, 0x00000042, 0x00800040, 0x00001042, 0x00000000, 0x00800002, 0x00000002, 0x00800042, 0x00801042, 0x00001002, 0x00801000, 0x00000040, 0x00000042, 0x00801040, 0x00801040, 0x00800042, 0x00001002, 0x00801000, 0x00001000, 0x00000002, 0x00800002, 0x00800040, 0x00800000, 0x00001040, 0x00801042, 0x00000000, 0x00001042, 0x00800000, 0x00000040, 0x00001002, 0x00800042, 0x00000040, 0x00000000, 0x00801042, 0x00801002, 0x00801040, 0x00000042, 0x00001000, 0x00001040, 0x00801002, 0x00800040, 0x00000042, 0x00000002, 0x00001042, 0x00801000, 0x00800002],
|
||||
[0x10400000, 0x00404010, 0x00000010, 0x10400010, 0x10004000, 0x00400000, 0x10400010, 0x00004010, 0x00400010, 0x00004000, 0x00404000, 0x10000000, 0x10404010, 0x10000010, 0x10000000, 0x10404000, 0x00000000, 0x10004000, 0x00404010, 0x00000010, 0x10000010, 0x10404010, 0x00004000, 0x10400000, 0x10404000, 0x00400010, 0x10004010, 0x00404000, 0x00004010, 0x00000000, 0x00400000, 0x10004010, 0x00404010, 0x00000010, 0x10000000, 0x00004000, 0x10000010, 0x10004000, 0x00404000, 0x10400010, 0x00000000, 0x00404010, 0x00004010, 0x10404000, 0x10004000, 0x00400000, 0x10404010, 0x10000000, 0x10004010, 0x10400000, 0x00400000, 0x10404010, 0x00004000, 0x00400010, 0x10400010, 0x00004010, 0x00400010, 0x00000000, 0x10404000, 0x10000010, 0x10400000, 0x10004010, 0x00000010, 0x00404000],
|
||||
[0x00208080, 0x00008000, 0x20200000, 0x20208080, 0x00200000, 0x20008080, 0x20008000, 0x20200000, 0x20008080, 0x00208080, 0x00208000, 0x20000080, 0x20200080, 0x00200000, 0x00000000, 0x20008000, 0x00008000, 0x20000000, 0x00200080, 0x00008080, 0x20208080, 0x00208000, 0x20000080, 0x00200080, 0x20000000, 0x00000080, 0x00008080, 0x20208000, 0x00000080, 0x20200080, 0x20208000, 0x00000000, 0x00000000, 0x20208080, 0x00200080, 0x20008000, 0x00208080, 0x00008000, 0x20000080, 0x00200080, 0x20208000, 0x00000080, 0x00008080, 0x20200000, 0x20008080, 0x20000000, 0x20200000, 0x00208000, 0x20208080, 0x00008080, 0x00208000, 0x20200080, 0x00200000, 0x20000080, 0x20008000, 0x00000000, 0x00008000, 0x00200000, 0x20200080, 0x00208080, 0x20000000, 0x20208000, 0x00000080, 0x20008080]
|
||||
]
|
||||
|
||||
DES_ROTORS = [0x22, 0x0D, 0x05, 0x2E, 0x2F, 0x12, 0x20, 0x29, 0x0B, 0x35, 0x21, 0x14, 0x0E, 0x24, 0x1E, 0x18, 0x31, 0x02, 0x0F, 0x25, 0x2A, 0x32, 0x00, 0x15, 0x26, 0x30, 0x06, 0x1A, 0x27, 0x04, 0x34, 0x19, 0x0C, 0x1B, 0x1F, 0x28, 0x01, 0x11, 0x1C, 0x1D, 0x17, 0x33, 0x23, 0x07, 0x03, 0x16, 0x09, 0x2B, 0x29, 0x14, 0x0C, 0x35, 0x36, 0x19, 0x27, 0x30, 0x12, 0x1F, 0x28, 0x1B, 0x15, 0x2B, 0x25, 0x00, 0x01, 0x09, 0x16, 0x2C, 0x31, 0x02, 0x07, 0x1C, 0x2D, 0x37, 0x0D, 0x21, 0x2E, 0x0B, 0x06, 0x20, 0x13, 0x22, 0x26, 0x2F, 0x08, 0x18, 0x23, 0x24, 0x1E, 0x03, 0x2A, 0x0E, 0x0A, 0x1D, 0x10, 0x32, 0x37, 0x22, 0x1A, 0x26, 0x0B, 0x27, 0x35, 0x05, 0x20, 0x2D, 0x36, 0x29, 0x23, 0x02, 0x33, 0x0E, 0x0F, 0x17, 0x24, 0x03, 0x08, 0x10, 0x15, 0x2A, 0x06, 0x0C, 0x1B, 0x2F, 0x1F, 0x19, 0x14, 0x2E, 0x21, 0x30, 0x34, 0x04, 0x16, 0x07, 0x31, 0x32, 0x2C, 0x11, 0x01, 0x1C, 0x18, 0x2B, 0x1E, 0x09, 0x0C, 0x30, 0x28, 0x34, 0x19, 0x35, 0x26, 0x13, 0x2E, 0x06, 0x0B, 0x37, 0x31, 0x10, 0x0A, 0x1C, 0x1D, 0x25, 0x32, 0x11, 0x16, 0x1E, 0x23, 0x01, 0x14, 0x1A, 0x29, 0x04, 0x2D, 0x27, 0x22, 0x1F, 0x2F, 0x05, 0x0D, 0x12, 0x24, 0x15, 0x08, 0x09, 0x03, 0x00, 0x0F, 0x2A, 0x07, 0x02, 0x2C, 0x17, 0x1A, 0x05, 0x36, 0x0D, 0x27, 0x26, 0x34, 0x21, 0x1F, 0x14, 0x19, 0x0C, 0x08, 0x1E, 0x18, 0x2A, 0x2B, 0x33, 0x09, 0x00, 0x24, 0x2C, 0x31, 0x0F, 0x22, 0x28, 0x37, 0x12, 0x06, 0x35, 0x30, 0x2D, 0x04, 0x13, 0x1B, 0x20, 0x32, 0x23, 0x16, 0x17, 0x11, 0x0E, 0x1D, 0x01, 0x15, 0x10, 0x03, 0x25, 0x28, 0x13, 0x0B, 0x1B, 0x35, 0x34, 0x0D, 0x2F, 0x2D, 0x22, 0x27, 0x1A, 0x16, 0x2C, 0x07, 0x01, 0x02, 0x0A, 0x17, 0x0E, 0x32, 0x03, 0x08, 0x1D, 0x30, 0x36, 0x0C, 0x20, 0x14, 0x26, 0x05, 0x06, 0x12, 0x21, 0x29, 0x2E, 0x09, 0x31, 0x24, 0x25, 0x00, 0x1C, 0x2B, 0x0F, 0x23, 0x1E, 0x11, 0x33, 0x36, 0x21, 0x19, 0x29, 0x26, 0x0D, 0x1B, 0x04, 0x06, 0x30, 0x35, 0x28, 0x24, 0x03, 0x15, 0x0F, 0x10, 0x18, 0x25, 0x1C, 0x09, 0x11, 0x16, 0x2B, 0x05, 0x0B, 0x1A, 0x2E, 0x22, 0x34, 0x13, 0x14, 0x20, 0x2F, 0x37, 0x1F, 0x17, 0x08, 0x32, 0x33, 0x0E, 0x2A, 0x02, 0x1D, 0x31, 0x2C, 0x00, 0x0A, 0x0B, 0x2F, 0x27, 0x37, 0x34, 0x1B, 0x29, 0x12, 0x14, 0x05, 0x26, 0x36, 0x32, 0x11, 0x23, 0x1D, 0x1E, 0x07, 0x33, 0x2A, 0x17, 0x00, 0x24, 0x02, 0x13, 0x19, 0x28, 0x1F, 0x30, 0x0D, 0x21, 0x22, 0x2E, 0x04, 0x0C, 0x2D, 0x25, 0x16, 0x09, 0x0A, 0x1C, 0x01, 0x10, 0x2B, 0x08, 0x03, 0x0E, 0x18, 0x12, 0x36, 0x2E, 0x05, 0x06, 0x22, 0x30, 0x19, 0x1B, 0x0C, 0x2D, 0x04, 0x02, 0x18, 0x2A, 0x24, 0x25, 0x0E, 0x03, 0x31, 0x1E, 0x07, 0x2B, 0x09, 0x1A, 0x20, 0x2F, 0x26, 0x37, 0x14, 0x28, 0x29, 0x35, 0x0B, 0x13, 0x34, 0x2C, 0x1D, 0x10, 0x11, 0x23, 0x08, 0x17, 0x32, 0x0F, 0x0A, 0x15, 0x00, 0x20, 0x0B, 0x1F, 0x13, 0x14, 0x30, 0x05, 0x27, 0x29, 0x1A, 0x06, 0x12, 0x10, 0x07, 0x01, 0x32, 0x33, 0x1C, 0x11, 0x08, 0x2C, 0x15, 0x02, 0x17, 0x28, 0x2E, 0x04, 0x34, 0x0C, 0x22, 0x36, 0x37, 0x26, 0x19, 0x21, 0x0D, 0x03, 0x2B, 0x1E, 0x00, 0x31, 0x16, 0x25, 0x09, 0x1D, 0x18, 0x23, 0x0E, 0x2E, 0x19, 0x2D, 0x21, 0x22, 0x05, 0x13, 0x35, 0x37, 0x28, 0x14, 0x20, 0x1E, 0x15, 0x0F, 0x09, 0x0A, 0x2A, 0x00, 0x16, 0x03, 0x23, 0x10, 0x25, 0x36, 0x1F, 0x12, 0x0D, 0x1A, 0x30, 0x0B, 0x0C, 0x34, 0x27, 0x2F, 0x1B, 0x11, 0x02, 0x2C, 0x0E, 0x08, 0x24, 0x33, 0x17, 0x2B, 0x07, 0x31, 0x1C, 0x1F, 0x27, 0x06, 0x2F, 0x30, 0x13, 0x21, 0x26, 0x0C, 0x36, 0x22, 0x2E, 0x2C, 0x23, 0x1D, 0x17, 0x18, 0x01, 0x0E, 0x24, 0x11, 0x31, 0x1E, 0x33, 0x0B, 0x2D, 0x20, 0x1B, 0x28, 0x05, 0x19, 0x1A, 0x0D, 0x35, 0x04, 0x29, 0x00, 0x10, 0x03, 0x1C, 0x16, 0x32, 0x0A, 0x25, 0x02, 0x15, 0x08, 0x2A, 0x2D, 0x35, 0x14, 0x04, 0x05, 0x21, 0x2F, 0x34, 0x1A, 0x0B, 0x30, 0x1F, 0x03, 0x31, 0x2B, 0x25, 0x07, 0x0F, 0x1C, 0x32, 0x00, 0x08, 0x2C, 0x0A, 0x19, 0x06, 0x2E, 0x29, 0x36, 0x13, 0x27, 0x28, 0x1B, 0x26, 0x12, 0x37, 0x0E, 0x1E, 0x11, 0x2A, 0x24, 0x09, 0x18, 0x33, 0x10, 0x23, 0x16, 0x01, 0x06, 0x26, 0x22, 0x12, 0x13, 0x2F, 0x04, 0x0D, 0x28, 0x19, 0x05, 0x2D, 0x11, 0x08, 0x02, 0x33, 0x15, 0x1D, 0x2A, 0x09, 0x0E, 0x16, 0x03, 0x18, 0x27, 0x14, 0x1F, 0x37, 0x0B, 0x21, 0x35, 0x36, 0x29, 0x34, 0x20, 0x0C, 0x1C, 0x2C, 0x00, 0x01, 0x32, 0x17, 0x07, 0x0A, 0x1E, 0x31, 0x24, 0x0F, 0x14, 0x34, 0x30, 0x20, 0x21, 0x04, 0x12, 0x1B, 0x36, 0x27, 0x13, 0x06, 0x00, 0x16, 0x10, 0x0A, 0x23, 0x2B, 0x01, 0x17, 0x1C, 0x24, 0x11, 0x07, 0x35, 0x22, 0x2D, 0x0C, 0x19, 0x2F, 0x26, 0x0B, 0x37, 0x0D, 0x2E, 0x1A, 0x2A, 0x03, 0x0E, 0x0F, 0x09, 0x25, 0x15, 0x18, 0x2C, 0x08, 0x32, 0x1D, 0x1B, 0x06, 0x37, 0x27, 0x28, 0x0B, 0x19, 0x22, 0x04, 0x2E, 0x1A, 0x0D, 0x07, 0x1D, 0x17, 0x11, 0x2A, 0x32, 0x08, 0x1E, 0x23, 0x2B, 0x18, 0x0E, 0x1F, 0x29, 0x34, 0x13, 0x20, 0x36, 0x2D, 0x12, 0x05, 0x14, 0x35, 0x21, 0x31, 0x0A, 0x15, 0x16, 0x10, 0x2C, 0x1C, 0x00, 0x33, 0x0F, 0x02, 0x24]
|
||||
byte_102D4AA0 = [0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x03, 0x02, 0x01, 0x00, 0x1F, 0x1E]
|
||||
|
||||
KEY_DATA = [0] * 96
|
||||
|
||||
|
||||
def roll_right(arg2, arg3):
|
||||
return (arg2 << 32 - arg3 | (arg2 >> arg3)) & 0xffffffff
|
||||
|
||||
|
||||
def des_small_flips_encrypt(key, output, uid):
|
||||
v3 = uid[0] | (uid[1] << 8) | (uid[2] << 16) | (uid[3] << 24)
|
||||
v0 = uid[4] | (uid[5] << 8) | (uid[6] << 16) | (uid[7] << 24)
|
||||
v4 = (16 * ((v3 ^ (v0 >> 4)) & 0xF0F0F0F)) ^ v0
|
||||
v5 = (v3 ^ (v0 >> 4)) & 0xF0F0F0F ^ v3
|
||||
v6 = (v4 ^ (v5 >> 16)) & 0xffff
|
||||
v7 = (v6 << 16) ^ v5
|
||||
v8 = v6 ^ v4
|
||||
v9 = (v7 ^ (v8 >> 2)) & 0x33333333
|
||||
v10 = (4 * v9) ^ v8
|
||||
v11 = v9 ^ v7
|
||||
v12 = (v10 ^ (v11 >> 8)) & 0xFF00FF
|
||||
v13 = (v12 << 8) ^ v11
|
||||
v14 = roll_right(v12 ^ v10, 1)
|
||||
v15 = (v13 ^ v14) & 0x55555555
|
||||
v16 = v15 ^ v14
|
||||
v17 = roll_right(v15 ^ v13, 1)
|
||||
|
||||
for i in range(0, 32, 4):
|
||||
v19 = roll_right(v17 ^ key[i + 1], 28)
|
||||
v16 ^= (0
|
||||
^ DES_KEYMAP[0][((v17 ^ key[i]) >> 26) & 0x3f]
|
||||
^ DES_KEYMAP[1][((v17 ^ key[i]) >> 18) & 0x3f]
|
||||
^ DES_KEYMAP[2][(((v17 ^ key[i]) >> 8) >> 2) & 0x3f]
|
||||
^ DES_KEYMAP[3][((v17 ^ key[i]) >> 2) & 0x3f]
|
||||
^ DES_KEYMAP[4][(v19 >> 26) & 0x3f]
|
||||
^ DES_KEYMAP[5][(v19 >> 18) & 0x3f]
|
||||
^ DES_KEYMAP[6][(v19 >> 10) & 0x3f]
|
||||
^ DES_KEYMAP[7][(v19 >> 2) & 0x3f]
|
||||
)
|
||||
v20 = roll_right(v16 ^ key[i + 3], 28)
|
||||
v17 ^= (0
|
||||
^ DES_KEYMAP[0][((v16 ^ key[i + 2]) >> 26) & 0x3f]
|
||||
^ DES_KEYMAP[1][((v16 ^ key[i + 2]) >> 18) & 0x3F]
|
||||
^ DES_KEYMAP[2][(((v16 ^ key[i + 2]) >> 8) >> 2) & 0x3f]
|
||||
^ DES_KEYMAP[3][((v16 ^ key[i + 2]) >> 2) & 0x3F]
|
||||
^ DES_KEYMAP[4][(v20 >> 26) & 0x3f]
|
||||
^ DES_KEYMAP[5][(v20 >> 18) & 0x3F]
|
||||
^ DES_KEYMAP[6][(v20 >> 10) & 0x3f]
|
||||
^ DES_KEYMAP[7][(v20 >> 2) & 0x3f]
|
||||
)
|
||||
v21 = roll_right(v16, 31)
|
||||
v22 = (v17 ^ v21) & 0x55555555
|
||||
v23 = v22 ^ v21
|
||||
v24 = roll_right(v22 ^ v17, 31)
|
||||
v25 = (v24 ^ (v23 >> 8)) & 0xFF00FF
|
||||
v26 = (v25 << 8) ^ v23
|
||||
v27 = v25 ^ v24
|
||||
v28 = (v26 ^ ((v25 ^ v24) >> 2)) & 0x33333333
|
||||
v29 = (4 * v28) ^ v27
|
||||
v30 = v28 ^ v26
|
||||
v31 = (v29 ^ (v30 >> 16)) & 0xffff
|
||||
v32 = ((v31 << 16) & 0xffffffff) ^ v30
|
||||
v33 = v31 ^ v29
|
||||
v34 = (v32 ^ (v33 >> 4)) & 0xF0F0F0F
|
||||
v35 = (v34 << 4) ^ v33
|
||||
v34 = v34 ^ v32
|
||||
|
||||
output[0] = v34 & 0xff
|
||||
output[1] = (v34 >> 8) & 0xff
|
||||
output[2] = (v34 >> 16) & 0xff
|
||||
output[3] = (v34 >> 24) & 0xff
|
||||
output[4] = v35 & 0xff
|
||||
output[5] = (v35 >> 8) & 0xff
|
||||
output[6] = (v35 >> 16) & 0xff
|
||||
output[7] = (v35 >> 24) & 0xff
|
||||
|
||||
|
||||
def des_small_flips_decrypt(key, output, uid):
|
||||
v3 = uid[0] | (uid[1] << 8) | (uid[2] << 16) | (uid[3] << 24)
|
||||
v0 = uid[4] | (uid[5] << 8) | (uid[6] << 16) | (uid[7] << 24)
|
||||
v4 = (16 * ((v3 ^ (v0 >> 4)) & 0xF0F0F0F)) ^ v0
|
||||
v5 = (v3 ^ (v0 >> 4)) & 0xF0F0F0F ^ v3
|
||||
v6 = (v4 ^ (v5 >> 16)) & 0xffff
|
||||
v7 = (v6 << 16) ^ v5
|
||||
v8 = v6 ^ v4
|
||||
v9 = (v7 ^ (v8 >> 2)) & 0x33333333
|
||||
v10 = (4 * v9) ^ v8
|
||||
v11 = v9 ^ v7
|
||||
v12 = (v10 ^ (v11 >> 8)) & 0xFF00FF
|
||||
v13 = (v12 << 8) ^ v11
|
||||
v14 = roll_right(v12 ^ v10, 1)
|
||||
v15 = (v13 ^ v14) & 0x55555555
|
||||
v16 = v15 ^ v14
|
||||
v17 = roll_right(v15 ^ v13, 1)
|
||||
|
||||
for i in range(0, 32, 4):
|
||||
v19 = roll_right(v17 ^ key[(31 - i)], 28)
|
||||
v16 ^= (0
|
||||
^ DES_KEYMAP[0][((v17 ^ key[(30 - i)]) >> 26) & 0x3f]
|
||||
^ DES_KEYMAP[1][((v17 ^ key[(30 - i)]) >> 18) & 0x3F]
|
||||
^ DES_KEYMAP[2][(((v17 ^ key[(30 - i)]) >> 8) >> 2) & 0x3f]
|
||||
^ DES_KEYMAP[3][((v17 ^ key[(30 - i)]) >> 2) & 0x3F]
|
||||
^ DES_KEYMAP[4][(v19 >> 26) & 0x3f]
|
||||
^ DES_KEYMAP[5][(v19 >> 18) & 0x3F]
|
||||
^ DES_KEYMAP[6][(v19 >> 10) & 0x3f]
|
||||
^ DES_KEYMAP[7][(v19 >> 2) & 0x3f]
|
||||
)
|
||||
v20 = roll_right(v16 ^ key[(29 - i)], 28)
|
||||
v17 ^= (0
|
||||
^ DES_KEYMAP[0][((v16 ^ key[(28 - i)]) >> 26) & 0x3f]
|
||||
^ DES_KEYMAP[1][((v16 ^ key[(28 - i)]) >> 18) & 0x3F]
|
||||
^ DES_KEYMAP[2][(((v16 ^ key[(28 - i)]) >> 8) >> 2) & 0x3f]
|
||||
^ DES_KEYMAP[3][((v16 ^ key[(28 - i)]) >> 2) & 0x3F]
|
||||
^ DES_KEYMAP[4][(v20 >> 26) & 0x3f]
|
||||
^ DES_KEYMAP[5][(v20 >> 18) & 0x3f]
|
||||
^ DES_KEYMAP[6][(v20 >> 10) & 0x3f]
|
||||
^ DES_KEYMAP[7][(v20 >> 2) & 0x3f]
|
||||
)
|
||||
|
||||
v21 = roll_right(v16, 31)
|
||||
v22 = (v17 ^ v21) & 0x55555555
|
||||
v23 = v22 ^ v21
|
||||
v24 = roll_right(v22 ^ v17, 31)
|
||||
v25 = (v24 ^ (v23 >> 8)) & 0xFF00FF
|
||||
v26 = (v25 << 8) ^ v23
|
||||
v27 = v25 ^ v24
|
||||
v28 = (v26 ^ ((v25 ^ v24) >> 2)) & 0x33333333
|
||||
v29 = (4 * v28) ^ v27
|
||||
v30 = v28 ^ v26
|
||||
v31 = (v29 ^ (v30 >> 16)) & 0xffff
|
||||
v32 = (v31 << 16) ^ v30
|
||||
v33 = v31 ^ v29
|
||||
v34 = (v32 ^ (v33 >> 4)) & 0xF0F0F0F
|
||||
v35 = (v34 << 4) ^ v33
|
||||
v34 = v34 ^ v32
|
||||
|
||||
output[0] = v34 & 0xff
|
||||
output[1] = (v34 >> 8) & 0xff
|
||||
output[2] = (v34 >> 16) & 0xff
|
||||
output[3] = (v34 >> 24) & 0xff
|
||||
output[4] = v35 & 0xff
|
||||
output[5] = (v35 >> 8) & 0xff
|
||||
output[6] = (v35 >> 16) & 0xff
|
||||
output[7] = (v35 >> 24) & 0xff
|
||||
|
||||
|
||||
def des3_encryption(key, kid_out, uid):
|
||||
des_small_flips_encrypt(key, kid_out, uid)
|
||||
des_small_flips_decrypt(key[32:], kid_out, kid_out)
|
||||
des_small_flips_encrypt(key[64:], kid_out, kid_out)
|
||||
|
||||
|
||||
def des_setkey(key, offset, out):
|
||||
b1 = bytearray(56);
|
||||
|
||||
for i in range(8):
|
||||
v3 = out[i]
|
||||
for j in range(7):
|
||||
b1[-7 * i - j + 55] = (v3 >> (j + 1)) & 1
|
||||
|
||||
for i in range(32):
|
||||
v5 = 0
|
||||
for j in range(24):
|
||||
v5 |= b1[DES_ROTORS[24 * i + j]] << byte_102D4AA0[24 * (i & 1) + j]
|
||||
|
||||
key[offset + i] = v5
|
||||
|
||||
|
||||
def des3_setkey(key, out):
|
||||
for i in range(3):
|
||||
des_setkey(key, 32 * i, out[8 * i:])
|
||||
|
||||
|
||||
def load_key(key):
|
||||
key_data = bytearray(24)
|
||||
for i in range(24):
|
||||
key_data[i] = 2 * key[i % len(key)]
|
||||
des3_setkey(KEY_DATA, key_data)</code></pre>
|
||||
</details>
|
||||
</details>
|
||||
|
||||
</body>
|
20
index.html
20
index.html
@ -42,6 +42,18 @@
|
||||
<p>If you're here because you work on one of those aforementioned closed source projects, hello! Feel free to share
|
||||
knowledge with the rest of the world, or point out corrections. Or don't; you do you.</p>
|
||||
|
||||
<h3>Code snippets</h3>
|
||||
<p>Across these pages there are a number of code snippets. They roughly break down into three categories:</p>
|
||||
<ul>
|
||||
<li>Assembly: Directly disassembled code from game binaries</li>
|
||||
<li>C: Either raw decompilation, or slightly cleaned up decompilation</li>
|
||||
<li>Python: Snippets from my local testing implementations</li>
|
||||
<li>Pseudocode: Used to illustrate some points. Note that it probably started life as Python before being
|
||||
pseudo'd</li>
|
||||
</ul>
|
||||
<p>If you yoink chunks of Python code, attribution is always appreciated, but consider it under CC0 (just don't be
|
||||
that person who tries to take credit for it, yeah?).</p>
|
||||
|
||||
<h2>Contents</h2>
|
||||
<ol>
|
||||
<li><a href="./transport.html">Transport layer</a></li>
|
||||
@ -56,6 +68,14 @@
|
||||
<li><a href="./packet.html#schema">Binary schemas</a></li>
|
||||
<li><a href="./packet.html#data">Binary data</a></li>
|
||||
</ol>
|
||||
<li><a href="./protocol.html">Communication protocol details</a></li>
|
||||
<ul>
|
||||
<li>There are a crazy number of sub pages here, so just go check the contents there.</li>
|
||||
</ul>
|
||||
<li>Misc pages</li>
|
||||
<ol>
|
||||
<li><a href="./cardid.html">Parsing and converting card IDs</a></li>
|
||||
</ol>
|
||||
</ol>
|
||||
|
||||
<h2>Getting started</h2>
|
||||
|
@ -159,11 +159,11 @@
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>0x40</code></td>
|
||||
<td><code>0xBF</code></td>
|
||||
<td><code>ISO-8859-1</code></td>
|
||||
<td><code>ISO_8859-1</code></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>0x60</code></td>
|
||||
|
@ -27,13 +27,13 @@
|
||||
are identified in requests using the <code>cardtype</code> attribute as in the below table.
|
||||
</p>
|
||||
<p>e-Amusement cards have a "card number" and a "card id". Confusingly, neither is a number. The card number is the
|
||||
one printed on your card. The card ID is your KONAMI ID. The number is derrived from your ID using an algorithm
|
||||
that I'll detail here once I get round to it.</p>
|
||||
one printed on your card. The card ID is your KONAMI ID. You can (and should) read about the algorithm used for
|
||||
these IDs on <a href="../cardid.html">the Card IDs page</a>.</p>
|
||||
<p>In the interest of not wasting space, <code>cardid</code> and <code>cardtype</code> will be omitted from
|
||||
individual breakdowns where their meaning is obvious.</p>
|
||||
|
||||
<h4>Card types:</h4>
|
||||
<table class="nocode">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td><code>cardtype</code></td>
|
||||
@ -69,7 +69,7 @@
|
||||
<pre><code><call <i>...</i>>
|
||||
<cardmng method="inquire" cardid="" cardtype="" update="" model*="" />
|
||||
</call></code></pre>
|
||||
<table class="nocode">
|
||||
<table>
|
||||
<tr>
|
||||
<td><code>update</code></td>
|
||||
<td>Should the tracked last play time be updated by this inquiry? (Just a guess)</td>
|
||||
@ -84,7 +84,7 @@
|
||||
seen for this specific game. If we have never seen this card used on this game, it is possible this card was
|
||||
used with an older version of this game, and migration is supported, in which case we report as if we had found
|
||||
a profile for this game.</p>
|
||||
<table class="nocode">
|
||||
<table>
|
||||
<tr>
|
||||
<td><code>refid</code></td>
|
||||
<td>A reference to this card to be used in other requests</td>
|
||||
@ -115,7 +115,7 @@
|
||||
<pre><code><call <i>...</i>>
|
||||
<cardmng method="getrefid" cardtype="" cardid=" newflag="" passwd="" model*="" />
|
||||
</call></code></pre>
|
||||
<table class="nocode">
|
||||
<table>
|
||||
<tr>
|
||||
<td><code>newflag</code></td>
|
||||
<td>?</td>
|
||||
@ -130,7 +130,7 @@
|
||||
<pre><code><response>
|
||||
<cardmng status="<i>status</i>" refid="" dataid="" pcode="" />
|
||||
</response></code></pre>
|
||||
<table class="nocode">
|
||||
<table>
|
||||
<tr>
|
||||
<td><code>refid</code></td>
|
||||
<td>A reference to this card to be used in other requests</td>
|
||||
@ -174,7 +174,7 @@
|
||||
<pre><code><call <i>...</i>>
|
||||
<cardmng method="authpass" refid="" pass="" model*="" />
|
||||
</call></code></pre>
|
||||
<table class="nocode">
|
||||
<table>
|
||||
<tr>
|
||||
<td><code>refid</code></td>
|
||||
<td>The reference we received either during <code>cardmng.inquire</code> or <code>cardmng.getrefid</code>
|
||||
|
Loading…
Reference in New Issue
Block a user