This commit is contained in:
Bottersnike 2021-12-28 20:54:12 +00:00
parent 20affcfa63
commit 2da6e2b056
48 changed files with 2508 additions and 2875 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.pyc
__pycache__/
build/

View File

@ -21,4 +21,4 @@
</table>
</body>
{% endblock %}

79
docs.py Normal file
View File

@ -0,0 +1,79 @@
from flask import Flask, send_from_directory, render_template
from livereload import Server
import os
app = Flask(__name__)
@app.route("/styles.css")
def styles():
return send_from_directory(".", "styles.css")
for base, folders, files in os.walk("images"):
for name in files:
def handler(base, name):
def handler():
return send_from_directory(base, name)
return handler
local_base = base.replace("\\", "/").strip(".").strip("/")
route = local_base + "/" + name
if not route.startswith("/"):
route = "/" + route
handler = handler(base, name)
handler.__name__ == route
app.add_url_rule(route, route, handler)
TEMPLATES = "templates"
PAGES_BASE = "pages"
for base, folders, files in os.walk(TEMPLATES + "/" + PAGES_BASE):
if ".git" in base:
continue
if base.startswith(TEMPLATES):
base = base[len(TEMPLATES):]
for name in files:
if name.endswith(".html"):
def handler(base, name):
def handler():
return render_template(os.path.join(base, name).strip("/").replace("\\", "/"))
return handler
local_base = base.replace("\\", "/").strip(".").strip("/")
if local_base.startswith(PAGES_BASE):
local_base = local_base[len(PAGES_BASE):]
route = local_base + "/" + name
if route.endswith("/index.html"):
route = route[:-10]
if not route.startswith("/"):
route = "/" + route
handler = handler(base, name)
handler.__name__ == route
app.add_url_rule(route, route, handler)
from flask import url_for
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
@app.route("/site-map")
def site_map():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
return str(links)
if __name__ == '__main__':
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config['DEBUG'] = True
server = Server(app.wsgi_app)
server.watch("templates")
server.serve(port=3000)

7
freeze.py Normal file
View File

@ -0,0 +1,7 @@
from flask_frozen import Freezer
from docs import app
freezer = Freezer(app)
if __name__ == '__main__':
freezer.freeze()

View File

@ -1,106 +0,0 @@
<!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>Benami/Konami e-Amusement API</h1>
<p>Why?</p>
<p>I was curious how these APIs work, yet could find little to nothing on Google. There are a number of
closed-source projects, with presumably similarly closed-source internal documentation, and a scattering of
implementations of things, yet I couldn't find a site that actually just documents how the API works. If I'm
going to have to reverse engineer an open source project (or a closed source one, for that matter), I might as
well just go reverse engineer an actual game (or it's stdlib, as most of my time has been spent currently).</p>
<p>For the sake of being lazy, I'll probably end up calling it eAmuse more than anything else throughout these
pages. Other names you may come across include <code>httac</code> and <code>xrpc</code>. The latter are the
suite of HTTP functions used in the Bemani stdlib, and the name of their communication protocol they implement
at the application layer, but whenever someone refers to any of them in the context of a rhythm game, they will
be referring to the things documented here.</p>
<p>These pages are very much a work in progress, and are being written <i>as</i> I reverse engineer parts of the
protocol. I've been asserting all my assumptions by writing my own implementation as I go, however it currently
isn't sharable quality code and, more importantly, the purpose of these pages is to make implementation of one's
own code hopefully trivial (teach a man to fish, and all that).</p>
<p>Sharing annotated sources for all of the games' stdlibs would be both impractical and unwise. Where relevant
however I try to include snippets to illustrate concepts, and have included their locations in the source for if
you feel like taking a dive too.</p>
<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>
<ol>
<li><a href="./transport.html#packet">Packet structure</a></li>
<li><a href="./transport.html#type">Types</a></li>
</ol>
<li><a href="./packet.html">The inner packet structure</a></li>
<ol>
<li><a href="./packet.html#xml">XML packets</a></li>
<li><a href="./packet.html#binary">Binary packed packets</a></li>
<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>
<p>My aim with these pages is to cover as much as possible, so you don't need to try and figure them out yourself.
That said, being able to follow along yourself will almost certainly help get more out of this. For following
along with source code, you're really going to want to grab yourself a dumped copy of a game (it's going to be a
lot easier, and cheeper, than dumping one yourself). I trust you can figure out where to find that.</p>
<p>For network related things, your options are a little broader. The ideal would be physical ownership of a
cabinet, and a subscription to genuine e-amusement. Odds are you don't have both of those :P. A connection to an
alternative network works just as well. In the more likely case that you don't have a physical cabinet, it's
time to crack out that dumped copy of a game and just run it on your own PC (or a VM, if you're not on Windows)
(odds are whatever you downloaded came with the program you'll need to start it pre-packaged. If not, it rhymes
with rice.).</p>
<p>You will also need a local e-amusement-emulating server. By the time I'm done with these pages, there will
hopefully be everything you need to be able to write your own. Unfortunately I'm not finished writing them;
depending on where you acquired your game, it may have shipped with one of said servers. If it didn't, Asphyxia
CORE will do the trick (yes, it's closed source).</p>
<p>If this all sounds like way too much work, and/or you're just here because of curiosity, I plan to prepare some
pcaps of network traffic to play around with without needing a running copy of a game or a network tap on a cab.
</p>
<a href="./transport.html">Next page</a>
<p><small>This site intentionally looks not-great. I don't feel like changing that, and honestly quite like the
aesthetic.</small></p>
</body>
</html>

View File

@ -1,896 +0,0 @@
<!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>Packet format | eAmuse 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>Packet format</h1>
<p>eAmuse uses XML for its application layer payloads*. This XML is either verbatim, or in a custom packed binary
format.<br /><small>*Newer games use JSON, but this page is about XML.</small></p>
<h2 id="xml">The XML format</h2>
<p>Each tag that contains a value has a <code>__type</code> attribute that identifies what type it is. Array types
have a <code>__count</code> attribute indicating how many items are in the array. Binary blobs additionally have
a <code>__size</code> attribute indicating their length (this is notably not present on strings, however).</p>
<p>It is perhaps simpler to illustrate with an example, so:</p>
<pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;call model="KFC:J:A:A:2019020600" srcid="1000" tag="b0312077"&gt;
&lt;eventlog method="write"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;data&gt;
&lt;eventid __type="str"&gt;G_CARDED&lt;/eventid&gt;
&lt;eventorder __type="s32"&gt;5&lt;/eventorder&gt;
&lt;pcbtime __type="u64"&gt;1639669516779&lt;/pcbtime&gt;
&lt;gamesession __type="s64"&gt;1&lt;/gamesession&gt;
&lt;strdata1 __type="str" /&gt;
&lt;strdata2 __type="str" /&gt;
&lt;numdata1 __type="s64"&gt;1&lt;/numdata1&gt;
&lt;numdata2 __type="s64" /&gt;
&lt;locationid __type="str"&gt;ea&lt;/locationid&gt;
&lt;/data&gt;
&lt;/eventlog&gt;
&lt;/call&gt;</code></pre>
<p>Arrays are encoded by concatenating every value together, with spaces between them. Data types that have multiple
values, are serialized similarly.</p>
<p>Therefore, an element storing an array of <code>3u8</code> (<code>[(1, 2, 3), (4, 5, 6)]</code>) would look like
this</p>
<pre><code>&lt;demo __type="3u8" __count="2"&gt;1 2 3 4 5 6&lt;/demo&gt;</code></pre>
<p>Besides this, this is otherwise a rather standard XML.</p>
<h2 id="binary">Packed binary overview</h2>
<p>Many packets, rather than using a string-based XML format, use a custom binary packed format instead. While it
can be a little confusing, remembering that this is encoding an XML tree can make it easier to parse.</p>
<p>To start with, let's take a look at the overall structure of the packets.</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><i>A0</i></td>
<td>C</td>
<td>E</td>
<td>~E</td>
<td colspan="4">Head length</td>
<td style="border-bottom: none" colspan="8"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none;" colspan="16">Schema definition</td>
</tr>
<tr>
<td style="border-top: none;" colspan="12"></td>
<td colspan="1"><i>FF</i></td>
<td colspan="3">Align</td>
</tr>
<tr>
<td colspan="4">Data length</td>
<td style="border-bottom: none" colspan="12"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none;" colspan="16">Payload</td>
</tr>
<tr>
<td style="border-top: none;" colspan="13"></td>
<td colspan="3">Align</td>
</tr>
</table>
<p>Every packet starts with the magic byte <code>0xA0</code>. Following this is the content byte, the encoding byte,
and then the 2's compliment of the encoding byte.</p>
<p>Currently known possible values for the content byte are:</p>
<table>
<thead>
<tr>
<td>C</td>
<td>Content</td>
</tr>
</thead>
<tr>
<td><code>0x42</code></td>
<td>Compressed data</td>
</tr>
<tr>
<td><code>0x43</code></td>
<td>Compressed, no data</td>
</tr>
<tr>
<td><code>0x45</code></td>
<td>Decompressed data</td>
</tr>
<tr>
<td><code>0x46</code></td>
<td>Decompressed, no data</td>
</tr>
</table>
<p>Decompressed packets contain an XML string. Compressed packets are what we're interested in here.</p>
<p>The encoding flag indicates the encoding for all string types in the packet (more on those later). Possible
values are:</p>
<table>
<thead>
<tr>
<td><code>E</code></td>
<td><code>~E</code></td>
<td colspan="3">Encoding name</td>
</tr>
</thead>
<tr>
<td><code>0x20</code></td>
<td><code>0xDF</code></td>
<td><code>ASCII</code></td>
<td></td>
<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>
</tr>
<tr>
<td><code>0x60</code></td>
<td><code>0x9F</code></td>
<td><code>EUC-JP</code></td>
<td><code>EUCJP</code></td>
<td><code>EUC_JP</code></td>
</tr>
<tr>
<td><code>0x80</code></td>
<td><code>0x7F</code></td>
<td><code>SHIFT-JIS</code></td>
<td><code>SHIFT_JIS</code></td>
<td><code>SJIS</code></td>
</tr>
<tr>
<td><code>0xA0</code></td>
<td><code>0x5F</code></td>
<td><code>UTF-8</code></td>
<td><code>UTF8</code></td>
<td></td>
</tr>
</table>
<details>
<summary>Source code details</summary>
<p>The full table for these values can be found in libavs.</p>
<figure>
<img src="./images/encoding_table.png">
<figcaption><code>libavs-win32.dll:0x1006b960</code></figcaption>
</figure>
<p>A second table exists just before this on in the source, responsible for the
<code>&lt;?xml version='1.0' encoding='??'?&gt;</code> line in XML files.
</p>
<figure>
<img src="./images/xml_encoding_table.png">
<figcaption><code>libavs-win32.dll:0x1006b940</code></figcaption>
</figure>
<p>This is indexed using the following function, which maps the above encoding IDs to 1, 2, 3, 4 and 5
respectively.</p>
<pre><code>char* xml_get_encoding_name(uint encoding_id) {
return ENCODING_NAME_TABLE[((encoding_id & 0xe0) >> 5) * 4];
}</code></pre>
</details>
<p>While validating <code>~E</code> isn't technically required, it acts as a useful assertion that the packet being
parsed is valid.</p>
<h2 id="schema">The packet schema header</h2>
<p>Following the 4 byte header, is a 4 byte integer containing the length of the next part of the header (this is
technically made redundant as this structure is also terminated).</p>
<p>This part of the header defines the schema that the main payload uses.</p>
<p>A tag definition looks like:</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>Type</td>
<td>nlen</td>
<td colspan="7">Tag name</td>
<td style="border-bottom: none" colspan="8"></td>
</tr>
<tr>
<td style="border-top: none;" colspan="15">Attributes and children</td>
<td colspan="1"><i>FE</i></td>
</tr>
</table>
<p>Structure names are encoded as densely packed 6 bit values, length prefixed (<code>nlen</code>). The acceptable
alphabet is <code>0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz</code>, and the packed values
are indecies within this alphabet.</p>
<p>The children can be a combination of either attribute names, or child tags. Attribute names are represented by
the byte <code>0x2E</code> followed by a length prefixed name as defined above. Child tags follow the above
format. Type <code>0x2E</code> must therefore be considered reserved as a possible structure type.</p>
<p>Attributes (type <code>0x2E</code>) represent a string attribute. Any other attribute must be defined as a child
tag. Is it notable that 0 children is allowable, which is how the majority of values are encoded.</p>
<p>All valid IDs, and their respective type, are listed in the following table. The bucket column here will be
used later when unpacking the main data, so we need not worry about it for now, but be warned it exists and is
possibly the least fun part of this format.</p>
<table class="code">
<thead>
<tr>
<td>ID</td>
<td>Bytes</td>
<td>C type</td>
<td>Bucket</td>
<td colspan="2">XML names</td>
<td></td>
<td>ID</td>
<td>Bytes</td>
<td>C type</td>
<td>Bucket</td>
<td colspan="2">XML names</td>
</tr>
</thead>
<tr>
<td>0x01</td>
<td>0</td>
<td>void</td>
<td>-</td>
<td>void</td>
<td></td>
<td></td>
<td>0x21</td>
<td>24</td>
<td>uint64[3]</td>
<td>int</td>
<td>3u64</td>
<td></td>
</tr>
<tr>
<td>0x02</td>
<td>1</td>
<td>int8</td>
<td>byte</td>
<td>s8</td>
<td></td>
<td></td>
<td>0x22</td>
<td>12</td>
<td>float[3]</td>
<td>int</td>
<td>3f</td>
<td></td>
</tr>
<tr>
<td>0x03</td>
<td>1</td>
<td>uint8</td>
<td>byte</td>
<td>u8</td>
<td></td>
<td></td>
<td>0x23</td>
<td>24</td>
<td>double[3]</td>
<td>int</td>
<td>3d</td>
<td></td>
</tr>
<tr>
<td>0x04</td>
<td>2</td>
<td>int16</td>
<td>short</td>
<td>s16</td>
<td></td>
<td></td>
<td>0x24</td>
<td>4</td>
<td>int8[4]</td>
<td>int</td>
<td>4s8</td>
<td></td>
</tr>
<tr>
<td>0x05</td>
<td>2</td>
<td>uint16</td>
<td>short</td>
<td>s16</td>
<td></td>
<td></td>
<td>0x25</td>
<td>4</td>
<td>uint8[4]</td>
<td>int</td>
<td>4u8</td>
<td></td>
</tr>
<tr>
<td>0x06</td>
<td>4</td>
<td>int32</td>
<td>int</td>
<td>s32</td>
<td></td>
<td></td>
<td>0x26</td>
<td>8</td>
<td>int16[4]</td>
<td>int</td>
<td>4s16</td>
<td></td>
</tr>
<tr>
<td>0x07</td>
<td>4</td>
<td>uint32</td>
<td>int</td>
<td>u32</td>
<td></td>
<td></td>
<td>0x27</td>
<td>8</td>
<td>uint8[4]</td>
<td>int</td>
<td>4s16</td>
<td></td>
</tr>
<tr>
<td>0x08</td>
<td>8</td>
<td>int64</td>
<td>int</td>
<td>s64</td>
<td></td>
<td></td>
<td>0x28</td>
<td>16</td>
<td>int32[4]</td>
<td>int</td>
<td>4s32</td>
<td>vs32</td>
</tr>
<tr>
<td>0x09</td>
<td>8</td>
<td>uint64</td>
<td>int</td>
<td>u64</td>
<td></td>
<td></td>
<td>0x29</td>
<td>16</td>
<td>uint32[4]</td>
<td>int</td>
<td>4u32</td>
<td>vs32</td>
</tr>
<tr>
<td>0x0a</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>bin</td>
<td>binary</td>
<td></td>
<td>0x2a</td>
<td>32</td>
<td>int64[4]</td>
<td>int</td>
<td>4s64</td>
<td></td>
</tr>
<tr>
<td>0x0b</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>str</td>
<td>string</td>
<td></td>
<td>0x2b</td>
<td>32</td>
<td>uint64[4]</td>
<td>int</td>
<td>4u64</td>
<td></td>
</tr>
<tr>
<td>0x0c</td>
<td>4</td>
<td>uint8[4]</td>
<td>int</td>
<td>ip4</td>
<td></td>
<td></td>
<td>0x2c</td>
<td>16</td>
<td>float[4]</td>
<td>int</td>
<td>4f</td>
<td>vf</td>
</tr>
<tr>
<td>0x0d</td>
<td>4</td>
<td>uint32</td>
<td>int</td>
<td>time</td>
<td></td>
<td></td>
<td>0x2d</td>
<td>32</td>
<td>double[4]</td>
<td>int</td>
<td>4d</td>
<td></td>
</tr>
<tr>
<td>0x0e</td>
<td>4</td>
<td>float</td>
<td>int</td>
<td>float</td>
<td>f</td>
<td></td>
<td>0x2e</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>attr</td>
<td></td>
</tr>
<tr>
<td>0x0f</td>
<td>8</td>
<td>double</td>
<td>int</td>
<td>double</td>
<td>d</td>
<td></td>
<td>0x2f</td>
<td>0</td>
<td></td>
<td>-</td>
<td>array</td>
<td></td>
</tr>
<tr>
<td>0x10</td>
<td>2</td>
<td>int8[2]</td>
<td>short</td>
<td>2s8</td>
<td></td>
<td></td>
<td>0x30</td>
<td>16</td>
<td>int8[16]</td>
<td>int</td>
<td>vs8</td>
<td></td>
</tr>
<tr>
<td>0x11</td>
<td>2</td>
<td>uint8[2]</td>
<td>short</td>
<td>2u8</td>
<td></td>
<td></td>
<td>0x31</td>
<td>16</td>
<td>uint8[16]</td>
<td>int</td>
<td>vu8</td>
<td></td>
</tr>
<tr>
<td>0x12</td>
<td>4</td>
<td>int16[2]</td>
<td>int</td>
<td>2s16</td>
<td></td>
<td></td>
<td>0x32</td>
<td>16</td>
<td>int8[8]</td>
<td>int</td>
<td>vs16</td>
<td></td>
</tr>
<tr>
<td>0x13</td>
<td>4</td>
<td>uint16[2]</td>
<td>int</td>
<td>2s16</td>
<td></td>
<td></td>
<td>0x33</td>
<td>16</td>
<td>uint8[8]</td>
<td>int</td>
<td>vu16</td>
<td></td>
</tr>
<tr>
<td>0x14</td>
<td>8</td>
<td>int32[2]</td>
<td>int</td>
<td>2s32</td>
<td></td>
<td></td>
<td>0x34</td>
<td>1</td>
<td>bool</td>
<td>byte</td>
<td>bool</td>
<td>b</td>
</tr>
<tr>
<td>0x15</td>
<td>8</td>
<td>uint32[2]</td>
<td>int</td>
<td>2u32</td>
<td></td>
<td></td>
<td>0x35</td>
<td>2</td>
<td>bool[2]</td>
<td>short</td>
<td>2b</td>
<td></td>
</tr>
<tr>
<td>0x16</td>
<td>16</td>
<td>int16[2]</td>
<td>int</td>
<td>2s64</td>
<td>vs64</td>
<td></td>
<td>0x36</td>
<td>3</td>
<td>bool[3]</td>
<td>int</td>
<td>3b</td>
<td></td>
</tr>
<tr>
<td>0x17</td>
<td>16</td>
<td>uint16[2]</td>
<td>int</td>
<td>2u64</td>
<td>vu64</td>
<td></td>
<td>0x37</td>
<td>4</td>
<td>bool[4]</td>
<td>int</td>
<td>4b</td>
<td></td>
</tr>
<tr>
<td>0x18</td>
<td>8</td>
<td>float[2]</td>
<td>int</td>
<td>2f</td>
<td></td>
<td></td>
<td>0x38</td>
<td>16</td>
<td>bool[16]</td>
<td>int</td>
<td>vb</td>
<td></td>
</tr>
<tr>
<td>0x19</td>
<td>16</td>
<td>double[2]</td>
<td>int</td>
<td>2d</td>
<td>vd</td>
<td></td>
<td>0x38</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1a</td>
<td>3</td>
<td>int8[3]</td>
<td>int</td>
<td>3s8</td>
<td></td>
<td></td>
<td>0x39</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1b</td>
<td>3</td>
<td>uint8[3]</td>
<td>int</td>
<td>3u8</td>
<td></td>
<td></td>
<td>0x3a</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1c</td>
<td>6</td>
<td>int16[3]</td>
<td>int</td>
<td>3s16</td>
<td></td>
<td></td>
<td>0x3b</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1d</td>
<td>6</td>
<td>uint16[3]</td>
<td>int</td>
<td>3s16</td>
<td></td>
<td></td>
<td>0x3c</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1e</td>
<td>12</td>
<td>int32[3]</td>
<td>int</td>
<td>3s32</td>
<td></td>
<td></td>
<td>0x3d</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1f</td>
<td>12</td>
<td>uint32[3]</td>
<td>int</td>
<td>3u32</td>
<td></td>
<td></td>
<td>0x3e</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x20</td>
<td>24</td>
<td>int64[3]</td>
<td>int</td>
<td>3s64</td>
<td></td>
<td></td>
<td>0x3f</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<p>Strings should be encoded and decoded according to the encoding specified in the packet header. Null termination is optional, however should be stripped during decoding.</p>
<p>All of these IDs are <code>& 0x3F</code>. Any value can be turned into an array by setting the 7<sup>th</sup> bit
high (<code>| 0x40</code>). Arrays of this form, in the data section, will be an aligned <code>size: u32</code>
immediately followed by <code>size</code> bytes' worth of (unaligned!) values of the unmasked type.</p>
<details>
<summary>Source code details</summary>
<p>The full table for these values can be found in libavs. This table contains the names of every tag, along
with additional information such as how many bytes that data type requires, and which parsing function
should be used.</p>
<figure>
<img src="./images/types_table.png">
<figcaption><code>libavs-win32.dll:0x100782a8</code></figcaption>
</figure>
</details>
<details>
<summary>Note about the <code>array</code> type:</summary>
<p>While I'm not totally sure, I have a suspicion this type is used internally as a pseudo-type. Trying to
identify its function as a parsable type has some obvious blockers:</p>
<p>All of the types have convenient <code>printf</code>-using helper functions that are used to emit them when
serializing XML. All except one.</p>
<img src="./images/no_array.png">
<p>If we have a look inside the function that populates node sizes (<code>libavs-win32.dll:0x1000cf00</code>),
it has an explicit case, however is the same fallback as the default case.</p>
<img src="./images/no_array_2.png">
<p>In the same function, however, we can find a second (technically first) check for the array type.</p>
<img src="./images/yes_array.png">
<p>This seems to suggest that internally arrays are represented as a normal node, with the <code>array</code>
type, however when serializing it's converted into the array types we're used to (well, will be after the
next sections) by masking 0x40 onto the contained type.</p>
<p>Also of interest from this snippet is the fact that <code>void</code>, <code>bin</code>, <code>str</code>,
and <code>attr</code> cannot be arrays. <code>void</code> and <code>attr</code> make sense, however
<code>str</code> and <code>bin</code> are more interesting. I suspect this is because konami want to be able
to preallocate the memory, which wouldn't be possible with these variable length structures.
</p>
</details>
<h2 id="data">The data section</h2>
<p>This is where all the actual packet data is. For the most part, parsing this is the easy part. We traverse our
schema, and read values out of the packet according to the value indicated in the schema. Unfortunately, konami
decided all data should be aligned very specifically, and that gaps left during alignment should be backfilled
later. This makes both reading and writing somewhat more complicated, however the system can be fairly easily
understood.</p>
<p>Firstly, we divide the payload up into 4 byte chunks. Each chunk can be allocated to either store individual
bytes, shorts, or ints (these are the buckets in the table above). When reading or writing a value, we first
check if a chunk allocated to the desired type's bucket is available and has free/as-yet-unread space within it.
If so, we will store/read our data to/from there. If there is no such chunk, we claim the next unclaimed chunk
for our bucket.</p>
<p>For example, imagine we write the sequence <code>byte, int, byte, short, byte, int, short</code>. The final output should look like:</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>byte</td>
<td>byte</td>
<td>byte</td>
<td></td>
<td colspan="4">int</td>
<td colspan="2">short</td>
<td colspan="2">short</td>
<td colspan="4">int</td>
</tr>
</table>
<p>While this might seem a silly system compared to just not aligning values, it is at least possible to intuit that it helps reduce wasted space. It should be noted that any variable-length structure, such as a string or an array, claims all chunks it encroaches on for the <code>int</code> bucket, disallowing the storage of bytes or shorts within them.</p>
<details>
<summary>Implementing a packer</summary>
<p>While the intuitive way to understand the packing algorithm is via chunks and buckets, a far more efficient implementation can be made that uses three pointers. Rather than try to explain in words, hopefully this python implementation should suffice as explanation:<pre><code>class Packer:
def __init__(self, offset=0):
self._word_cursor = offset
self._short_cursor = offset
self._byte_cursor = offset
self._boundary = offset % 4
def _next_block(self):
self._word_cursor += 4
return self._word_cursor - 4
def request_allocation(self, size):
if size == 0:
return self._word_cursor
elif size == 1:
if self._byte_cursor % 4 == self._boundary:
self._byte_cursor = self._next_block() + 1
else:
self._byte_cursor += 1
return self._byte_cursor - 1
elif size == 2:
if self._short_cursor % 4 == self._boundary:
self._short_cursor = self._next_block() + 2
else:
self._short_cursor += 2
return self._short_cursor - 2
else:
old_cursor = self._word_cursor
for _ in range(math.ceil(size / 4)):
self._word_cursor += 4
return old_cursor
def notify_skipped(self, no_bytes):
for _ in range(math.ceil(no_bytes / 4)):
self.request_allocation(4)</code></pre></p>
</details>
<a href="./transport.html">Prev page</a> | <a href="./protocol.html">Next page</a>
</body>
</html>

View File

@ -1,33 +0,0 @@
<!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><code>apsmanager</code></h1>
<h2 id="getstat"><code>apsmanager.getstat</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;apsmanager method="getstat" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;apsmanager status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,232 +0,0 @@
<!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><code>cardmng</code></h1>
<p>As the name might imply, this service is responsible for handling interactions with physical e-Amusement cards.
e-Amusement currently has two different types of cards in circulation. There are classic e-Amusement cards
making use of a magnetic stripe, and the newer RFID cards using FeliCa (these are probably what you have). They
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. 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>
<thead>
<tr>
<td><code>cardtype</code></td>
<td>Meaning</td>
</tr>
</thead>
<tr>
<td><code>1</code></td>
<td>Old style magnetic stripe card</td>
</tr>
<tr>
<td><code>2</code></td>
<td>FeliCa RFID card</td>
</tr>
</table>
<ul>
<li><code><a href="#inquire">cardmng.inquire</a></code></li>
<li><code><a href="#getrefid">cardmng.getrefid</a></code></li>
<li><code><a href="#bindmodel">cardmng.bindmodel</a></code></li>
<li><code><a href="#bindcard">cardmng.bindcard</a></code></li>
<li><code><a href="#authpass">cardmng.authpass</a></code></li>
<li><code><a href="#getkeepspan">cardmng.getkeepspan</a></code></li>
<li><code><a href="#getkeepremain">cardmng.getkeepremain</a></code></li>
<li><code><a href="#getdatalist">cardmng.getdatalist</a></code></li>
</ul>
<h2 id="inquire"><code>cardmng.inquire</code></h2>
<p>Request information about a card that has been inserted or touched against a reader.</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="inquire" cardid="" cardtype="" update="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>update</code></td>
<td>Should the tracked last play time be updated by this inquiry? (Just a guess)</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" refid="" dataid="" pcode="" newflag="" binded="" expired=" ecflag="" useridflag="" extidflag="" lastupdate="" /&gt;
&lt;/response&gt;</code></pre>
<p>To handle this request, we first must lookup if this <code>cardid</code> has ever been seen by our servers
before. If not, we abort with a <code>112</code> status. Otherwise, we proceeed to check if this card has been
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>
<tr>
<td><code>refid</code></td>
<td>A reference to this card to be used in other requests</td>
</tr>
<tr>
<td><code>dataid</code></td>
<td>Appears to be set the same as <code>refid</code>; presumably to allow different keys for game state vs
login details.</td>
</tr>
<tr>
<td><code>newflag</code></td>
<td>Inverse of <code>binded</code></td>
</tr>
<tr>
<td><code>binded</code></td>
<td>Has a profile ever been created for this game (or an older version, requiring a migration)
(<code>1</code> or <code>0</code>)</td>
</tr>
<tr>
<td><code>expired</code></td>
<td>? Just set to <code>0</code>.</td>
</tr>
</table>
<h2 id="getrefid"><code>cardmng.getrefid</code></h2>
<p>Register a new card to this server.</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getrefid" cardtype="" cardid=" newflag="" passwd="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>newflag</code></td>
<td>?</td>
</tr>
<tr>
<td><code>passwd</code></td>
<td>The pin for this new user. <i>Should</i> always be a four digit number (and that's worth validating),
but it's passed as a string so could feasibly be anything desired.</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" refid="" dataid="" pcode="" /&gt;
&lt;/response&gt;</code></pre>
<table>
<tr>
<td><code>refid</code></td>
<td>A reference to this card to be used in other requests</td>
</tr>
<tr>
<td><code>dataid</code></td>
<td>Appears to be set the same as <code>refid</code>; presumably to allow different keys for game state vs
login details.</td>
</tr>
<tr>
<td><code>pcode</code></td>
<td>? Not present in captured data.</td>
</tr>
</table>
<h2 id="bindmodel"><code>cardmng.bindmodel</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="bindmodel" refid="" newflag="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" dataid="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="bindcard"><code>cardmng.bindcard</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="bindcard" cardtype="" newid="" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="authpass"><code>cardmng.authpass</code></h2>
<p>Test a pin for a card. This request notably uses the <code>refid</code>, so required a
<code>cardmng.inquire</code> call to be made first.
</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="authpass" refid="" pass="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>refid</code></td>
<td>The reference we received either during <code>cardmng.inquire</code> or <code>cardmng.getrefid</code>
(the latter for new cards)</td>
</tr>
<tr>
<td><code>pass</code></td>
<td>The pin to test. See <code>cardmng.getrefid</code>.</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<p>If the pin is valid, status should be <code>0</code>. Otherwise, <code>116</code>.</p>
<h2 id="getkeepspan"><code>cardmng.getkeepspan</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getkeepspan" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" keepspan="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getkeepremain"><code>cardmng.getkeepremain</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getkeepremain" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" keepremain="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getdatalist"><code>cardmng.getdatalist</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getdatalist" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>"&gt;
&lt;item[]&gt;
&lt;mcode __type="str" /&gt;
&lt;dataid __type="str" /&gt;
&lt;regtime __type="str" /&gt;
&lt;lasttime __type="str" /&gt;
&lt;exptime __type="str" /&gt;
&lt;expflag __type="u8" /&gt;
&lt;/item[]&gt;
&lt;/cardmng&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,54 +0,0 @@
<!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><code>dlstatus</code></h1>
<h2 id="done"><code>dlstatus.done</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;dlstatus method="done"&gt;
&lt;url&gt;
&lt;param __type="str" /&gt;
&lt;/url&gt;
&lt;name __type="str" /&gt;
&lt;size __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;dlstatus status="<i>status</i>"&gt;
&lt;progress __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/response&gt;</code></pre>
<h2 id="progress"><code>dlstatus.progress</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;dlstatus method="progress" /&gt;
&lt;progress __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;dlstatus status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,37 +0,0 @@
<!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><code>esign</code></h1>
<h2 id="request"><code>esign.request</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esign method="request"&gt;
<i>placeholder</i>
&lt;/esign&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esign status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/esign&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,50 +0,0 @@
<!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><code>esoc</code></h1>
<h2 id="read"><code>esoc.read</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esoc method="read"&gt;
&lt;senddata /&gt;
&lt;/esoc&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esoc status="<i>status</i>"&gt;
&lt;recvdata /&gt;
&lt;/esoc&gt;
&lt;/response&gt;</code></pre>
<p>Go figure.</p>
<h2 id="write"><code>esoc.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esoc method="write"&gt;
&lt;senddata /&gt;
&lt;/esoc&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esoc status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,58 +0,0 @@
<!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><code>eventlog</code></h1>
<h2 id="write"><code>eventlog.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eventlog method="write"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;data&gt;
&lt;eventid __type="str" /&gt;
&lt;eventorder __type="s32" /&gt;
&lt;pcbtime __type="u64" /&gt;
&lt;gamesession __type="s64" /&gt;
&lt;strdata1 __type="str" /&gt;
&lt;strdata2 __type="str" /&gt;
&lt;numdata1 __type="s64" /&gt;
&lt;numdata2 __type="s64" /&gt;
&lt;locationid __type="str" /&gt;
&lt;/data&gt;
&lt;/eventlog&gt;
&lt;/call&gt;</code></pre>
<p>Event ID list:</p>
<ul>
<li><code>G_GAMED</code></li>
<li><code>S_ERROR</code></li>
<li><code>S_PWRON</code> <b>TODO: find more!</b></li>
<li><code>T_OTDEMO</code></li>
</ul>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eventlog status="<i>status</i>"&gt;
&lt;gamesession __type="s64" /&gt;
&lt;logsendflg __type="s32" /&gt;
&lt;logerrlevel __type="s32" /&gt;
&lt;evtidnosendflg __type="s32" /&gt;
&lt;/eventlog&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,35 +0,0 @@
<!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><code>message</code></h1>
<h2 id="get"><code>message.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;message method="get" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;message expire="" status="<i>status</i>"&gt;
&lt;item[] name="" start="" end="" data="" /&gt;
&lt;/message&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,47 +0,0 @@
<!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><code>package</code></h1>
<h2 id="list"><code>package.list</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;package method="list" pkgtype="<i>pkgtype</i>" model*="" /&gt;
&lt;/call&gt;</code></pre>
<p><code>all</code> is the only currently observed value for <code>pkgtype</code></p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;package status="<i>status</i>"&gt;
&lt;item[] url="" /&gt;
&lt;/package&gt;
&lt;/response&gt;</code></pre>
<p>A list of all packages available for download.</p>
<h2 id="intend"><code>package.intend</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;package method="intend" url="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;package status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,41 +0,0 @@
<!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><code>pcbevent</code></h1>
<h2 id="put"><code>pcbevent.put</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;pcbevent method="put"&gt;
&lt;time __type="time" /&gt;
&lt;seq __type="u32" /&gt;
&lt;item[]&gt;
&lt;name __type="str" /&gt;
&lt;value __type="s32" /&gt;
&lt;time __type="time" /&gt;
&lt;/item[]&gt;
&lt;/pcbevent&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;pcbevent status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,39 +0,0 @@
<!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><code>pcbtracker</code></h1>
<h2 id="alive"><code>pcbtracker.alive</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;pcbtracker method="alive" model*="" hardid="" softid="" accountid="" agree="" ecflag="" /&gt;
&lt;/call&gt;</code></pre>
<p><code>ecflag</code> here is determining if the arcade operator allows the use of paseli on this machine.</p>
<p><code>agree@</code> and <code>ecflag@</code> appear to either be totally non present, or present with a value of
<code>"1"</code>, but then again I may be reading the code wrong, so take that with a pinch of salt.
</p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;pcbtracker status="" time="" limit="" ecenable="" eclimit="" &gt;
&lt;/response&gt;</code></pre>
<p>As you might guess, <code>ecenable@</code> is therefore the flag to determine if paseli is enabled (i.e. the
arcade operator and the server both allow its use).</p>
</body>

View File

@ -1,70 +0,0 @@
<!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><code>services</code></h1>
<h2 id="get"><code>services.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;services method="get" model*="" &gt;
&lt;info&gt;
&lt;AVS2 __type="str"&gt;<i>AVS2 version</i>&lt;/AVS2&gt;
&lt;/info&gt;
&lt;/services&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;services expire="" method="get" mode="" status="<i>status</i>"&gt;
&lt;item[] name="<i>service</i>" url="<i>url</i>" /&gt;
&lt;/services&gt;
&lt;/response&gt;</code></pre>
<p>Known services are:</p>
<ul>
<li><code>ntp</code></li>
<li><code>keepalive</code></li>
<li><code>cardmng</code></li>
<li><code>facility</code></li>
<li><code>message</code></li>
<li><code>numbering</code></li>
<li><code>package</code></li>
<li><code>pcbevent</code></li>
<li><code>pcbtracker</code></li>
<li><code>pkglist</code></li>
<li><code>posevent</code></li>
<li><code>userdata</code></li>
<li><code>userid</code></li>
<li><code>eacoin</code></li>
<li><code>local</code></li>
<li><code>local2</code></li>
<li><code>lobby</code></li>
<li><code>lobby2</code></li>
<li><code>dlstatus</code></li>
<li><code>netlog</code></li>
<li><code>sidmgr</code></li>
<li><code>globby</code></li>
</ul>
<p>Most of these will usually just return the URL to the eAmuse server (or your fake one ;D). <code>ntp</code> is a
notable exception, unless you're planning on reimplementing NTP. <code>keepalive</code> will likely alsop be a
custom URL with query parameters pre-baked.</p>
<p><code>mode</code> is one of <code>operation</code>, <code>debug</code>, <code>test</code>, or
<code>factory</code>.
</p>
</body>

View File

@ -1,94 +0,0 @@
<!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><code>sidmgr</code></h1>
<h2 id="create"><code>sidmgr.create</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="create"&gt;
&lt;cardtype __type="str" /&gt;
&lt;cardid __type="str" /&gt;
&lt;cardgid __type="str" /&gt;
&lt;steal __type="u8" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>"&gt;
&lt;state __type="u32" /&gt;
&lt;e_count __type="u8" /&gt;
&lt;last __type="time" /&gt;
&lt;locked __type="time" /&gt;
&lt;sid __type="str" /&gt;
&lt;cardid_status __type="u8" /&gt;
&lt;refid __type="str" /&gt;
&lt;/sidmgr&gt;
&lt;/response&gt;</code></pre>
<h2 id="open"><code>sidmgr.open</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="open" sid="" &gt;
&lt;pass __type="str" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>"&gt;
&lt;state __type="u32" /&gt;
&lt;refid __type="str" /&gt;
&lt;locked __type="time" /&gt;
&lt;/sidmgr&gt;
&lt;/response&gt;</code></pre>
<h2 id="touch"><code>sidmgr.touch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="touch" sid="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="branch"><code>sidmgr.branch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="branch" sid="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="close"><code>sidmgr.close</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="close" sid="" /&gt;
&lt;cause __type="u32" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,40 +0,0 @@
<!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><code>traceroute</code></h1>
<h2 id="send"><code>traceroute.send</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;traceroute proto="" method="send"&gt;
&lt;hop[]&gt;
&lt;valid __type="bool"&gt;
&lt;addr __type="ip4"&gt;
&lt;usec __type="u64"&gt;
&lt;/hop[]&gt;
&lt;/traceroute&gt;
&lt;/call&gt;</code></pre>
<p><code>hop</code> repeats for every hop (unsurprisingly)</p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;traceroute status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,48 +0,0 @@
<!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><code>userdata</code></h1>
<h2 id="read"><code>userdata.read</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;userdata method="read" card*="" model*="" label="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;userdata status="<i>status</i>" time=""&gt;
&lt;b[] __type="" /&gt;
&lt;/userdata&gt;
&lt;/response&gt;</code></pre>
<p><code>__type</code> here can be either <code>bin</code> or <code>str</code></p>
<h2 id="write"><code>userdata.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;userdata method="write" card="" time="" model*="" label*="" &gt;
&lt;b[] __type="str" /&gt;
&lt;/userdata&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;userdata status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,284 +0,0 @@
<!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>Packet format | eAmuse 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>Application Protocol</h1>
<p>As the previous pages have eluded to (you <i>did</i> read them, didn't you?), eAmuse uses HTTP as its main way of
getting data around. This means we need an HTTP server running but, as we'll see, we don't need to think too
hard about that.</p>
<p>Every request made is a <code>POST</code> request, to <code>//&lt;model&gt;/&lt;module&gt;/&lt;method&gt;</code>,
with its body being encoded data as described in the previous sections. In addition to the
<code>X-Compress:</code> and <code>X-Eamuse-Info:</code> headers previously detailed, there is also a
<code>X-PCB-ID:</code> header. that can be set. Your machine's PCB ID uniquely defines the physical board. This
header is added in out-bound requests, and allows the server to identify you. Importantly, it's also the value
that the server uses to identify which machines are authorized to be on the network, and which are not.
</p>
<p>Every request is followed immediately by a response. Any response code other than <code>200</code> is considered
a failure.</p>
<details>
<summary>Source code details</summary>
<figure>
<img src="images/200_only.png">
<figcaption><code>libavs-win32-ea3.dll:0x1000f8e7</code></figcaption>
</figure>
</details>
<p>All requests follow a basic format:</p>
<pre><code>&lt;call model="<i>model</i>" srcid="<i>srcid</i>" tag="<i>tag</i>"&gt;
&lt;<i>module</i> method="<i>method</i>" <i>...attributes</i>&gt;
<i>children</i>
&lt;/<i>module</i>&gt;
&lt;/call&gt;</code></pre>
<p>The responses follow a similar format:</p>
<pre><code>&lt;response&gt;
&lt;<i>module</i> status="<i>status</i>" <i>...attributes</i>&gt;
<i>children</i>
&lt;/<i>module</i>&gt;
&lt;/response&gt;</code></pre>
<p>With <code>"0"</code> being a successful status. Convention is to identify a specific method as
<code><i>module</i>.<i>method</i></code>, and we'll be following this convention in this document too. There are
a <i>lot</i> of possible methods, so the majority of this document is a big reference for them all. There are a
number of generic methods, and a number of game specific ones. If you haven't clocked yet, I've been working on
an SDVX 4 build for most of these pages, and each game also comes with its own set of game-specific methods.
These are namespaces under the <code>game.%s</code> module and, in the case of SDVX 4, are all
<code>game.sv4_<i>method</i></code>. I may or may not document the SDVX 4 specific methods, but I've listed them
here anyway for completeness.
</p>
<p>Paths in the XML bodies are formatted using an XPath-like syntax. That is, <code>status@/response</code> gets the
<code>status</code> attribute from <code>response</code>, and <code>response/eacoin/sequence</code> would return
that node's value.
</p>
<p><b>NOTE:</b> I am using the non-standard notation of <code>&lt;node* ...</code> and
<code>&lt;node attr*="" ...</code> to indicate that an attribute or node is not always present! Additionally, I
am going to use the notation of <code>&lt;node[]&gt;</code> to indicate that a node repeats.
</p>
<table>
<thead>
<tr>
<td>Status</td>
<td>Meaning</td>
</tr>
</thead>
<tr>
<td><code>0</code></td>
<td>Success</td>
</tr>
<tr>
<td><code>109</code></td>
<td>No profile</td>
</tr>
<tr>
<td><code>110</code></td>
<td>Not allowed</td>
</tr>
<tr>
<td><code>112</code></td>
<td>Card not found (<code>cardmng.inquire</code>)</td>
</tr>
<tr>
<td><code>116</code></td>
<td>Card pin invalid (<code>cardmng.authpass</code>)</td>
</tr>
</table>
<details>
<summary>How to reverse engineer these calls</summary>
<p>Turns out bemani have been quite sensible in how they implemented their code for creating structures, so it's
rather readable. That said, if you've been using Ghidra (like me!), this is the time to switch to IDA. I'll
let the below screenshots below speak for themselves:
</p>
<details>
<summary>Ghidra</summary>
<img src="images/eventlog_ghidra.png">
<img src="images/matching_request_ghidra.png">
</details>
<details>
<summary>IDA Pro</summary>
<img src="images/eventlog_ida.png">
<img src="images/matching_request_ida.png">
</details>
<p>I know which of these I'd rather use for reverse engineering (sorry, Ghidra)!</p>
</details>
<h2>Possible XRPC requests</h2>
<ul>
<li><code><a href="proto/eventlog.html">eventlog.%s</a></code></li>
<ul>
<li><code><a href="proto/eventlog.html#eventlog.write">eventlog.write</a></code></li>
</ul>
<li><code><a href="proto/playerdata.html">playerdata.%s</a></code></li>
<ul>
<li><code><a href="proto/playerdata.html#usergamedata_send">playerdata.usergamedata_send</a></code></li>
<li><code><a href="proto/playerdata.html#usergamedata_recv">playerdata.usergamedata_recv</a></code></li>
<li><code><a href="proto/playerdata.html#usergamedata_inheritance">playerdata.usergamedata_inheritance</a></code>
</li>
<li><code><a href="proto/playerdata.html#usergamedata_condrecv">playerdata.usergamedata_condrecv</a></code>
</li>
<li><code><a href="proto/playerdata.html#usergamedata_scorerank">playerdata.usergamedata_scorerank</a></code>
</li>
</ul>
<li><code><a href="proto/matching.html">matching.%s</a></code></li>
<ul>
<li><code><a href="proto/matching.html#request">matching.request</a></code></li>
<li><code><a href="proto/matching.html#wait">matching.wait</a></code></li>
<li><code><a href="proto/matching.html#finish">matching.finish</a></code></li>
</ul>
<li><code><a href="proto/system.html">system.%s</a></code></li>
<ul>
<li><code><a href="proto/system.html#getmaster">system.getmaster</a></code></li>
<li><code><a href="proto/system.html#getlocationiplist">system.getlocationiplist</a></code></li>
<li><code><a href="proto/system.html#xrpcproxy">system.xrpcproxy</a></code></li>
<li><code><a href="proto/system.html#convcardnumber">system.convcardnumber</a></code></li>
</ul>
<li><code><a href="proto/esoc.html">esoc.%s</a></code></li>
<ul>
<li><code><a href="proto/esoc.html#read">esoc.read</a></code></li>
<li><code><a href="proto/esoc.html#write">esoc.write</a></code></li>
</ul>
<li><code><a href="proto/cardmng.html">cardmng.%s</a></code></li>
<ul>
<li><code><a href="proto/cardmng.html#inquire">cardmng.inquire</a></code></li>
<li><code><a href="proto/cardmng.html#getrefid">cardmng.getrefid</a></code></li>
<li><code><a href="proto/cardmng.html#bindmodel">cardmng.bindmodel</a></code></li>
<li><code><a href="proto/cardmng.html#bindcard">cardmng.bindcard</a></code></li>
<li><code><a href="proto/cardmng.html#authpass">cardmng.authpass</a></code></li>
<li><code><a href="proto/cardmng.html#getkeepspan">cardmng.getkeepspan</a></code></li>
<li><code><a href="proto/cardmng.html#getkeepremain">cardmng.getkeepremain</a></code></li>
<li><code><a href="proto/cardmng.html#getdatalist">cardmng.getdatalist</a></code></li>
</ul>
<li><code><a href="proto/esign.html">esign.%s</a></code></li>
<ul>
<li><code><a href="proto/esign.html#request">esign.request</a></code></li>
</ul>
<li><code><a href="proto/package.html">package.%s</a></code></li>
<ul>
<li><code><a href="proto/package.html#list">package.list</a></code></li>
<li><code><a href="proto/package.html#intend">package.intend</a></code></li>
</ul>
<li><code><a href="proto/userdata.html">userdata.%s</a></code></li>
<ul>
<li><code><a href="proto/userdata.html#read">userdata.read</a></code></li>
<li><code><a href="proto/userdata.html#write">userdata.write</a></code></li>
</ul>
<li><code><a href="proto/services.html">services.%s</a></code></li>
<ul>
<li><code><a href="proto/services.html#get">services.get</a></code></li>
</ul>
<li><code><a href="proto/pcbtracker.html">pcbtracker.%s</a></code></li>
<ul>
<li><code><a href="proto/pcbtracker.html#alive">pcbtracker.alive</a></code></li>
</ul>
<li><code><a href="proto/pcbevent.html">pcbevent.%s</a></code></li>
<ul>
<li><code><a href="proto/pcbevent.html#put">pcbevent.put</a></code></li>
</ul>
<li><code><a href="proto/message.html">message.%s</a></code></li>
<ul>
<li><code><a href="proto/message.html#get">message.get</a></code></li>
</ul>
<li><code><a href="proto/facility.html">facility.%s</a></code></li>
<ul>
<li><code><a href="proto/facility.html#get">facility.get</a></code></li>
</ul>
<li><code><a href="proto/apsmanager.html">apsmanager.%s</a></code></li>
<ul>
<li><code><a href="proto/apsmanager.html#getstat">apsmanager.getstat</a></code></li>
</ul>
<li><code><a href="proto/sidmgr.html">sidmgr.%s</a></code></li>
<ul>
<li><code><a href="proto/sidmgr.html#create">sidmgr.create</a></code></li>
<li><code><a href="proto/sidmgr.html#open">sidmgr.open</a></code></li>
<li><code><a href="proto/sidmgr.html#touch">sidmgr.touch</a></code></li>
<li><code><a href="proto/sidmgr.html#branch">sidmgr.branch</a></code></li>
<li><code><a href="proto/sidmgr.html#close">sidmgr.close</a></code></li>
</ul>
<li><code><a href="proto/dlstatus.html">dlstatus.%s</a></code></li>
<ul>
<li><code><a href="proto/dlstatus.html#done">dlstatus.done</a></code></li>
<li><code><a href="proto/dlstatus.html#progress">dlstatus.progress</a></code></li>
</ul>
<li><code><a href="proto/eacoin.html">eacoin.%s</a></code></li>
<ul>
<li><code><a href="proto/eacoin.html#checkin">eacoin.checkin</a></code></li>
<li><code><a href="proto/eacoin.html#checkout">eacoin.checkout</a></code></li>
<li><code><a href="proto/eacoin.html#consume">eacoin.consume</a></code></li>
<li><code><a href="proto/eacoin.html#getbalance">eacoin.getbalance</a></code></li>
<li><code><a href="proto/eacoin.html#getecstatus">eacoin.getecstatus</a></code></li>
<li><code><a href="proto/eacoin.html#touch">eacoin.touch</a></code></li>
<li><code><a href="proto/eacoin.html#opchpass">eacoin.opchpass</a></code></li>
<li><code><a href="proto/eacoin.html#opcheckin">eacoin.opcheckin</a></code></li>
<li><code><a href="proto/eacoin.html#opcheckout">eacoin.opcheckout</a></code></li>
<li><code><a href="proto/eacoin.html#getlog">eacoin.getlog</a></code></li>
</ul>
<li><code><a href="proto/traceroute.html">traceroute.%s</a></code></li>
<ul>
<li><code><a href="proto/traceroute.html#send">traceroute.send</a></code></li>
</ul>
<li><code><a href="proto/game/sv4.html">game.%s</a></code></li>
<ul>
<li><code><a href="proto/game/sv4.html#sample">game.sv4_sample</a></code></li>
<li><code><a href="proto/game/sv4.html#new">game.sv4_new</a></code></li>
<li><code><a href="proto/game/sv4.html#load">game.sv4_load</a></code></li>
<li><code><a href="proto/game/sv4.html#load_m">game.sv4_load_m</a></code></li>
<li><code><a href="proto/game/sv4.html#save">game.sv4_save</a></code></li>
<li><code><a href="proto/game/sv4.html#save_m">game.sv4_save_m</a></code></li>
<li><code><a href="proto/game/sv4.html#common">game.sv4_common</a></code></li>
<li><code><a href="proto/game/sv4.html#shop">game.sv4_shop</a></code></li>
<li><code><a href="proto/game/sv4.html#hiscore">game.sv4_hiscore</a></code></li>
<li><code><a href="proto/game/sv4.html#buy">game.sv4_buy</a></code></li>
<li><code><a href="proto/game/sv4.html#exception">game.sv4_exception</a></code></li>
<li><code><a href="proto/game/sv4.html#entry_s">game.sv4_entry_s</a></code></li>
<li><code><a href="proto/game/sv4.html#entry_e">game.sv4_entry_e</a></code></li>
<li><code><a href="proto/game/sv4.html#frozen">game.sv4_frozen</a></code></li>
<li><code><a href="proto/game/sv4.html#lounce">game.sv4_lounce</a></code></li>
<li><code><a href="proto/game/sv4.html#save_e">game.sv4_save_e</a></code></li>
<li><code><a href="proto/game/sv4.html#save_pb">game.sv4_save_pb</a></code></li>
<li><code><a href="proto/game/sv4.html#save_c">game.sv4_save_c</a></code></li>
<li><code><a href="proto/game/sv4.html#play_s">game.sv4_play_s</a></code></li>
<li><code><a href="proto/game/sv4.html#play_e">game.sv4_play_e</a></code></li>
<li><code><a href="proto/game/sv4.html#serial">game.sv4_serial</a></code></li>
<li><code><a href="proto/game/sv4.html#save_fi">game.sv4_save_fi</a></code></li>
<li><code><a href="proto/game/sv4.html#print">game.sv4_print</a></code></li>
<li><code><a href="proto/game/sv4.html#print_h">game.sv4_print_h</a></code></li>
<li><code><a href="proto/game/sv4.html#load_r">game.sv4_load_r</a></code></li>
<li><code><a href="proto/game/sv4.html#save_campaign">game.sv4_save_campaign</a></code></li>
</ul>
</ul>
<b>Totally undocumented services (based on <code>services.get</code>):</b>
<ul>
<li><code>numbering</code></li>
<li><code>pkglist</code></li>
<li><code>userid</code></li>
<li><code>local</code></li>
<li><code>local2</code></li>
<li><code>lobby</code></li>
<li><code>lobby2</code></li>
<li><code>netlog</code></li>
<li><code>globby</code></li>
</ul>
<p>I'll try and figure these out in due course, promise!</p>
</body>

25
templates/base.html Normal file
View File

@ -0,0 +1,25 @@
<!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>{% block title %}{% endblock %}{% if self.title() %} | {% endif %}e-Amusement API</title>
<link rel="stylesheet" href="{{ROOT}}/styles.css">
</head>
<body>
<table>
<tr>
<td><a href="{{ROOT}}/">Contents</a></td>
<td><a href="{{ROOT}}/transport.html">Transport layer</a></td>
<td><a href="{{ROOT}}/packet.html">Packet format</a></td>
<td><a href="{{ROOT}}/protocol.html">Application Protocol</a></td>
</tr>
</table>
{% block body %}{% endblock %}
</body>
</html>

View File

@ -1,34 +1,14 @@
<!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
{% extends "base.html" %}
{% block body %}
<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
@ -39,183 +19,183 @@ ALPHABET = "0123456789ABCDEFGHJKLMNPRSTUWXYZ"
def enc_des(uid):
cipher = DES3.new(_KEY, DES3.MODE_CBC, iv=b'\0' * 8)
return cipher.encrypt(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)
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))
chk = sum(data[i] * (i % 3 + 1) for i in range(15))
while chk > 31:
chk = (chk >> 5) + (chk & 31)
while chk > 31:
chk = (chk >> 5) + (chk & 31)
return chk
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))
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))
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"
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")
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"
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 = 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)
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)
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")
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"
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]
for i in range(13, 0, -1):
card[i] ^= card[i - 1]
card[0] ^= card_type
card[0] ^= card_type
card_id = dec_des(pack_5(card[:13])[:8])[::-1]
card_id = binascii.hexlify(card_id).decode().upper()
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 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"
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>
</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 colspan="13">e-Amusement ID</td>
<td>Check byte</td>
<td>Card type</td>
<td>Checksum</td>
<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>
</table>
</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>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:
<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 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>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):
<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))
@ -226,39 +206,39 @@ def unpack_5(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>
<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
<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>
<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):
<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)
@ -268,31 +248,31 @@ card[15] = <a href="#checksum">checksum(card)</a></code></pre>
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
<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>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 = [
<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],
@ -482,7 +462,6 @@ def load_key(key):
for i in range(24):
key_data[i] = 2 * key[i % len(key)]
des3_setkey(KEY_DATA, key_data)</code></pre>
</details>
</details>
</body>
</details>
{% endblock %}

View File

@ -0,0 +1,84 @@
{% extends "base.html" %}
{% block body %}
<h1>Benami/Konami e-Amusement API</h1>
<p>Why?</p>
<p>I was curious how these APIs work, yet could find little to nothing on Google. There are a number of
closed-source projects, with presumably similarly closed-source internal documentation, and a scattering of
implementations of things, yet I couldn't find a site that actually just documents how the API works. If I'm
going to have to reverse engineer an open source project (or a closed source one, for that matter), I might as
well just go reverse engineer an actual game (or it's stdlib, as most of my time has been spent currently).</p>
<p>For the sake of being lazy, I'll probably end up calling it eAmuse more than anything else throughout these
pages. Other names you may come across include <code>httac</code> and <code>xrpc</code>. The latter are the
suite of HTTP functions used in the Bemani stdlib, and the name of their communication protocol they implement
at the application layer, but whenever someone refers to any of them in the context of a rhythm game, they will
be referring to the things documented here.</p>
<p>These pages are very much a work in progress, and are being written <i>as</i> I reverse engineer parts of the
protocol. I've been asserting all my assumptions by writing my own implementation as I go, however it currently
isn't sharable quality code and, more importantly, the purpose of these pages is to make implementation of one's
own code hopefully trivial (teach a man to fish, and all that).</p>
<p>Sharing annotated sources for all of the games' stdlibs would be both impractical and unwise. Where relevant
however I try to include snippets to illustrate concepts, and have included their locations in the source for if
you feel like taking a dive too.</p>
<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>
<ol>
<li><a href="./transport.html#packet">Packet structure</a></li>
<li><a href="./transport.html#type">Types</a></li>
</ol>
<li><a href="./packet.html">The inner packet structure</a></li>
<ol>
<li><a href="./packet.html#xml">XML packets</a></li>
<li><a href="./packet.html#binary">Binary packed packets</a></li>
<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>
<p>My aim with these pages is to cover as much as possible, so you don't need to try and figure them out yourself.
That said, being able to follow along yourself will almost certainly help get more out of this. For following
along with source code, you're really going to want to grab yourself a dumped copy of a game (it's going to be a
lot easier, and cheeper, than dumping one yourself). I trust you can figure out where to find that.</p>
<p>For network related things, your options are a little broader. The ideal would be physical ownership of a
cabinet, and a subscription to genuine e-amusement. Odds are you don't have both of those :P. A connection to an
alternative network works just as well. In the more likely case that you don't have a physical cabinet, it's
time to crack out that dumped copy of a game and just run it on your own PC (or a VM, if you're not on Windows)
(odds are whatever you downloaded came with the program you'll need to start it pre-packaged. If not, it rhymes
with rice.).</p>
<p>You will also need a local e-amusement-emulating server. By the time I'm done with these pages, there will
hopefully be everything you need to be able to write your own. Unfortunately I'm not finished writing them;
depending on where you acquired your game, it may have shipped with one of said servers. If it didn't, Asphyxia
CORE will do the trick (yes, it's closed source).</p>
<p>If this all sounds like way too much work, and/or you're just here because of curiosity, I plan to prepare some
pcaps of network traffic to play around with without needing a running copy of a game or a network tap on a cab.
</p>
<a href="./transport.html">Next page</a>
<p><small>This site intentionally looks not-great. I don't feel like changing that, and honestly quite like the
aesthetic.</small></p>
{% endblock %}

881
templates/pages/packet.html Normal file
View File

@ -0,0 +1,881 @@
{% extends "base.html" %}
{% block body %}
<h1>Packet format</h1>
<p>eAmuse uses XML for its application layer payloads*. This XML is either verbatim, or in a custom packed binary
format.<br /><small>*Newer games use JSON, but this page is about XML.</small></p>
<h2 id="xml">The XML format</h2>
<p>Each tag that contains a value has a <code>__type</code> attribute that identifies what type it is. Array types
have a <code>__count</code> attribute indicating how many items are in the array. Binary blobs additionally have
a <code>__size</code> attribute indicating their length (this is notably not present on strings, however).</p>
<p>It is perhaps simpler to illustrate with an example, so:</p>
<pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;call model="KFC:J:A:A:2019020600" srcid="1000" tag="b0312077"&gt;
&lt;eventlog method="write"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;data&gt;
&lt;eventid __type="str"&gt;G_CARDED&lt;/eventid&gt;
&lt;eventorder __type="s32"&gt;5&lt;/eventorder&gt;
&lt;pcbtime __type="u64"&gt;1639669516779&lt;/pcbtime&gt;
&lt;gamesession __type="s64"&gt;1&lt;/gamesession&gt;
&lt;strdata1 __type="str" /&gt;
&lt;strdata2 __type="str" /&gt;
&lt;numdata1 __type="s64"&gt;1&lt;/numdata1&gt;
&lt;numdata2 __type="s64" /&gt;
&lt;locationid __type="str"&gt;ea&lt;/locationid&gt;
&lt;/data&gt;
&lt;/eventlog&gt;
&lt;/call&gt;</code></pre>
<p>Arrays are encoded by concatenating every value together, with spaces between them. Data types that have multiple
values, are serialized similarly.</p>
<p>Therefore, an element storing an array of <code>3u8</code> (<code>[(1, 2, 3), (4, 5, 6)]</code>) would look like
this</p>
<pre><code>&lt;demo __type="3u8" __count="2"&gt;1 2 3 4 5 6&lt;/demo&gt;</code></pre>
<p>Besides this, this is otherwise a rather standard XML.</p>
<h2 id="binary">Packed binary overview</h2>
<p>Many packets, rather than using a string-based XML format, use a custom binary packed format instead. While it
can be a little confusing, remembering that this is encoding an XML tree can make it easier to parse.</p>
<p>To start with, let's take a look at the overall structure of the packets.</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><i>A0</i></td>
<td>C</td>
<td>E</td>
<td>~E</td>
<td colspan="4">Head length</td>
<td style="border-bottom: none" colspan="8"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none;" colspan="16">Schema definition</td>
</tr>
<tr>
<td style="border-top: none;" colspan="12"></td>
<td colspan="1"><i>FF</i></td>
<td colspan="3">Align</td>
</tr>
<tr>
<td colspan="4">Data length</td>
<td style="border-bottom: none" colspan="12"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none;" colspan="16">Payload</td>
</tr>
<tr>
<td style="border-top: none;" colspan="13"></td>
<td colspan="3">Align</td>
</tr>
</table>
<p>Every packet starts with the magic byte <code>0xA0</code>. Following this is the content byte, the encoding byte,
and then the 2's compliment of the encoding byte.</p>
<p>Currently known possible values for the content byte are:</p>
<table>
<thead>
<tr>
<td>C</td>
<td>Content</td>
</tr>
</thead>
<tr>
<td><code>0x42</code></td>
<td>Compressed data</td>
</tr>
<tr>
<td><code>0x43</code></td>
<td>Compressed, no data</td>
</tr>
<tr>
<td><code>0x45</code></td>
<td>Decompressed data</td>
</tr>
<tr>
<td><code>0x46</code></td>
<td>Decompressed, no data</td>
</tr>
</table>
<p>Decompressed packets contain an XML string. Compressed packets are what we're interested in here.</p>
<p>The encoding flag indicates the encoding for all string types in the packet (more on those later). Possible
values are:</p>
<table>
<thead>
<tr>
<td><code>E</code></td>
<td><code>~E</code></td>
<td colspan="3">Encoding name</td>
</tr>
</thead>
<tr>
<td><code>0x20</code></td>
<td><code>0xDF</code></td>
<td><code>ASCII</code></td>
<td></td>
<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>
</tr>
<tr>
<td><code>0x60</code></td>
<td><code>0x9F</code></td>
<td><code>EUC-JP</code></td>
<td><code>EUCJP</code></td>
<td><code>EUC_JP</code></td>
</tr>
<tr>
<td><code>0x80</code></td>
<td><code>0x7F</code></td>
<td><code>SHIFT-JIS</code></td>
<td><code>SHIFT_JIS</code></td>
<td><code>SJIS</code></td>
</tr>
<tr>
<td><code>0xA0</code></td>
<td><code>0x5F</code></td>
<td><code>UTF-8</code></td>
<td><code>UTF8</code></td>
<td></td>
</tr>
</table>
<details>
<summary>Source code details</summary>
<p>The full table for these values can be found in libavs.</p>
<figure>
<img src="./images/encoding_table.png">
<figcaption><code>libavs-win32.dll:0x1006b960</code></figcaption>
</figure>
<p>A second table exists just before this on in the source, responsible for the
<code>&lt;?xml version='1.0' encoding='??'?&gt;</code> line in XML files.
</p>
<figure>
<img src="./images/xml_encoding_table.png">
<figcaption><code>libavs-win32.dll:0x1006b940</code></figcaption>
</figure>
<p>This is indexed using the following function, which maps the above encoding IDs to 1, 2, 3, 4 and 5
respectively.</p>
<pre><code>char* xml_get_encoding_name(uint encoding_id) {
return ENCODING_NAME_TABLE[((encoding_id & 0xe0) >> 5) * 4];
}</code></pre>
</details>
<p>While validating <code>~E</code> isn't technically required, it acts as a useful assertion that the packet being
parsed is valid.</p>
<h2 id="schema">The packet schema header</h2>
<p>Following the 4 byte header, is a 4 byte integer containing the length of the next part of the header (this is
technically made redundant as this structure is also terminated).</p>
<p>This part of the header defines the schema that the main payload uses.</p>
<p>A tag definition looks like:</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>Type</td>
<td>nlen</td>
<td colspan="7">Tag name</td>
<td style="border-bottom: none" colspan="8"></td>
</tr>
<tr>
<td style="border-top: none;" colspan="15">Attributes and children</td>
<td colspan="1"><i>FE</i></td>
</tr>
</table>
<p>Structure names are encoded as densely packed 6 bit values, length prefixed (<code>nlen</code>). The acceptable
alphabet is <code>0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz</code>, and the packed values
are indecies within this alphabet.</p>
<p>The children can be a combination of either attribute names, or child tags. Attribute names are represented by
the byte <code>0x2E</code> followed by a length prefixed name as defined above. Child tags follow the above
format. Type <code>0x2E</code> must therefore be considered reserved as a possible structure type.</p>
<p>Attributes (type <code>0x2E</code>) represent a string attribute. Any other attribute must be defined as a child
tag. Is it notable that 0 children is allowable, which is how the majority of values are encoded.</p>
<p>All valid IDs, and their respective type, are listed in the following table. The bucket column here will be
used later when unpacking the main data, so we need not worry about it for now, but be warned it exists and is
possibly the least fun part of this format.</p>
<table class="code">
<thead>
<tr>
<td>ID</td>
<td>Bytes</td>
<td>C type</td>
<td>Bucket</td>
<td colspan="2">XML names</td>
<td></td>
<td>ID</td>
<td>Bytes</td>
<td>C type</td>
<td>Bucket</td>
<td colspan="2">XML names</td>
</tr>
</thead>
<tr>
<td>0x01</td>
<td>0</td>
<td>void</td>
<td>-</td>
<td>void</td>
<td></td>
<td></td>
<td>0x21</td>
<td>24</td>
<td>uint64[3]</td>
<td>int</td>
<td>3u64</td>
<td></td>
</tr>
<tr>
<td>0x02</td>
<td>1</td>
<td>int8</td>
<td>byte</td>
<td>s8</td>
<td></td>
<td></td>
<td>0x22</td>
<td>12</td>
<td>float[3]</td>
<td>int</td>
<td>3f</td>
<td></td>
</tr>
<tr>
<td>0x03</td>
<td>1</td>
<td>uint8</td>
<td>byte</td>
<td>u8</td>
<td></td>
<td></td>
<td>0x23</td>
<td>24</td>
<td>double[3]</td>
<td>int</td>
<td>3d</td>
<td></td>
</tr>
<tr>
<td>0x04</td>
<td>2</td>
<td>int16</td>
<td>short</td>
<td>s16</td>
<td></td>
<td></td>
<td>0x24</td>
<td>4</td>
<td>int8[4]</td>
<td>int</td>
<td>4s8</td>
<td></td>
</tr>
<tr>
<td>0x05</td>
<td>2</td>
<td>uint16</td>
<td>short</td>
<td>s16</td>
<td></td>
<td></td>
<td>0x25</td>
<td>4</td>
<td>uint8[4]</td>
<td>int</td>
<td>4u8</td>
<td></td>
</tr>
<tr>
<td>0x06</td>
<td>4</td>
<td>int32</td>
<td>int</td>
<td>s32</td>
<td></td>
<td></td>
<td>0x26</td>
<td>8</td>
<td>int16[4]</td>
<td>int</td>
<td>4s16</td>
<td></td>
</tr>
<tr>
<td>0x07</td>
<td>4</td>
<td>uint32</td>
<td>int</td>
<td>u32</td>
<td></td>
<td></td>
<td>0x27</td>
<td>8</td>
<td>uint8[4]</td>
<td>int</td>
<td>4s16</td>
<td></td>
</tr>
<tr>
<td>0x08</td>
<td>8</td>
<td>int64</td>
<td>int</td>
<td>s64</td>
<td></td>
<td></td>
<td>0x28</td>
<td>16</td>
<td>int32[4]</td>
<td>int</td>
<td>4s32</td>
<td>vs32</td>
</tr>
<tr>
<td>0x09</td>
<td>8</td>
<td>uint64</td>
<td>int</td>
<td>u64</td>
<td></td>
<td></td>
<td>0x29</td>
<td>16</td>
<td>uint32[4]</td>
<td>int</td>
<td>4u32</td>
<td>vs32</td>
</tr>
<tr>
<td>0x0a</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>bin</td>
<td>binary</td>
<td></td>
<td>0x2a</td>
<td>32</td>
<td>int64[4]</td>
<td>int</td>
<td>4s64</td>
<td></td>
</tr>
<tr>
<td>0x0b</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>str</td>
<td>string</td>
<td></td>
<td>0x2b</td>
<td>32</td>
<td>uint64[4]</td>
<td>int</td>
<td>4u64</td>
<td></td>
</tr>
<tr>
<td>0x0c</td>
<td>4</td>
<td>uint8[4]</td>
<td>int</td>
<td>ip4</td>
<td></td>
<td></td>
<td>0x2c</td>
<td>16</td>
<td>float[4]</td>
<td>int</td>
<td>4f</td>
<td>vf</td>
</tr>
<tr>
<td>0x0d</td>
<td>4</td>
<td>uint32</td>
<td>int</td>
<td>time</td>
<td></td>
<td></td>
<td>0x2d</td>
<td>32</td>
<td>double[4]</td>
<td>int</td>
<td>4d</td>
<td></td>
</tr>
<tr>
<td>0x0e</td>
<td>4</td>
<td>float</td>
<td>int</td>
<td>float</td>
<td>f</td>
<td></td>
<td>0x2e</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>attr</td>
<td></td>
</tr>
<tr>
<td>0x0f</td>
<td>8</td>
<td>double</td>
<td>int</td>
<td>double</td>
<td>d</td>
<td></td>
<td>0x2f</td>
<td>0</td>
<td></td>
<td>-</td>
<td>array</td>
<td></td>
</tr>
<tr>
<td>0x10</td>
<td>2</td>
<td>int8[2]</td>
<td>short</td>
<td>2s8</td>
<td></td>
<td></td>
<td>0x30</td>
<td>16</td>
<td>int8[16]</td>
<td>int</td>
<td>vs8</td>
<td></td>
</tr>
<tr>
<td>0x11</td>
<td>2</td>
<td>uint8[2]</td>
<td>short</td>
<td>2u8</td>
<td></td>
<td></td>
<td>0x31</td>
<td>16</td>
<td>uint8[16]</td>
<td>int</td>
<td>vu8</td>
<td></td>
</tr>
<tr>
<td>0x12</td>
<td>4</td>
<td>int16[2]</td>
<td>int</td>
<td>2s16</td>
<td></td>
<td></td>
<td>0x32</td>
<td>16</td>
<td>int8[8]</td>
<td>int</td>
<td>vs16</td>
<td></td>
</tr>
<tr>
<td>0x13</td>
<td>4</td>
<td>uint16[2]</td>
<td>int</td>
<td>2s16</td>
<td></td>
<td></td>
<td>0x33</td>
<td>16</td>
<td>uint8[8]</td>
<td>int</td>
<td>vu16</td>
<td></td>
</tr>
<tr>
<td>0x14</td>
<td>8</td>
<td>int32[2]</td>
<td>int</td>
<td>2s32</td>
<td></td>
<td></td>
<td>0x34</td>
<td>1</td>
<td>bool</td>
<td>byte</td>
<td>bool</td>
<td>b</td>
</tr>
<tr>
<td>0x15</td>
<td>8</td>
<td>uint32[2]</td>
<td>int</td>
<td>2u32</td>
<td></td>
<td></td>
<td>0x35</td>
<td>2</td>
<td>bool[2]</td>
<td>short</td>
<td>2b</td>
<td></td>
</tr>
<tr>
<td>0x16</td>
<td>16</td>
<td>int16[2]</td>
<td>int</td>
<td>2s64</td>
<td>vs64</td>
<td></td>
<td>0x36</td>
<td>3</td>
<td>bool[3]</td>
<td>int</td>
<td>3b</td>
<td></td>
</tr>
<tr>
<td>0x17</td>
<td>16</td>
<td>uint16[2]</td>
<td>int</td>
<td>2u64</td>
<td>vu64</td>
<td></td>
<td>0x37</td>
<td>4</td>
<td>bool[4]</td>
<td>int</td>
<td>4b</td>
<td></td>
</tr>
<tr>
<td>0x18</td>
<td>8</td>
<td>float[2]</td>
<td>int</td>
<td>2f</td>
<td></td>
<td></td>
<td>0x38</td>
<td>16</td>
<td>bool[16]</td>
<td>int</td>
<td>vb</td>
<td></td>
</tr>
<tr>
<td>0x19</td>
<td>16</td>
<td>double[2]</td>
<td>int</td>
<td>2d</td>
<td>vd</td>
<td></td>
<td>0x38</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1a</td>
<td>3</td>
<td>int8[3]</td>
<td>int</td>
<td>3s8</td>
<td></td>
<td></td>
<td>0x39</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1b</td>
<td>3</td>
<td>uint8[3]</td>
<td>int</td>
<td>3u8</td>
<td></td>
<td></td>
<td>0x3a</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1c</td>
<td>6</td>
<td>int16[3]</td>
<td>int</td>
<td>3s16</td>
<td></td>
<td></td>
<td>0x3b</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1d</td>
<td>6</td>
<td>uint16[3]</td>
<td>int</td>
<td>3s16</td>
<td></td>
<td></td>
<td>0x3c</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1e</td>
<td>12</td>
<td>int32[3]</td>
<td>int</td>
<td>3s32</td>
<td></td>
<td></td>
<td>0x3d</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1f</td>
<td>12</td>
<td>uint32[3]</td>
<td>int</td>
<td>3u32</td>
<td></td>
<td></td>
<td>0x3e</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x20</td>
<td>24</td>
<td>int64[3]</td>
<td>int</td>
<td>3s64</td>
<td></td>
<td></td>
<td>0x3f</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<p>Strings should be encoded and decoded according to the encoding specified in the packet header. Null termination is
optional, however should be stripped during decoding.</p>
<p>All of these IDs are <code>& 0x3F</code>. Any value can be turned into an array by setting the 7<sup>th</sup> bit
high (<code>| 0x40</code>). Arrays of this form, in the data section, will be an aligned <code>size: u32</code>
immediately followed by <code>size</code> bytes' worth of (unaligned!) values of the unmasked type.</p>
<details>
<summary>Source code details</summary>
<p>The full table for these values can be found in libavs. This table contains the names of every tag, along
with additional information such as how many bytes that data type requires, and which parsing function
should be used.</p>
<figure>
<img src="./images/types_table.png">
<figcaption><code>libavs-win32.dll:0x100782a8</code></figcaption>
</figure>
</details>
<details>
<summary>Note about the <code>array</code> type:</summary>
<p>While I'm not totally sure, I have a suspicion this type is used internally as a pseudo-type. Trying to
identify its function as a parsable type has some obvious blockers:</p>
<p>All of the types have convenient <code>printf</code>-using helper functions that are used to emit them when
serializing XML. All except one.</p>
<img src="./images/no_array.png">
<p>If we have a look inside the function that populates node sizes (<code>libavs-win32.dll:0x1000cf00</code>),
it has an explicit case, however is the same fallback as the default case.</p>
<img src="./images/no_array_2.png">
<p>In the same function, however, we can find a second (technically first) check for the array type.</p>
<img src="./images/yes_array.png">
<p>This seems to suggest that internally arrays are represented as a normal node, with the <code>array</code>
type, however when serializing it's converted into the array types we're used to (well, will be after the
next sections) by masking 0x40 onto the contained type.</p>
<p>Also of interest from this snippet is the fact that <code>void</code>, <code>bin</code>, <code>str</code>,
and <code>attr</code> cannot be arrays. <code>void</code> and <code>attr</code> make sense, however
<code>str</code> and <code>bin</code> are more interesting. I suspect this is because konami want to be able
to preallocate the memory, which wouldn't be possible with these variable length structures.
</p>
</details>
<h2 id="data">The data section</h2>
<p>This is where all the actual packet data is. For the most part, parsing this is the easy part. We traverse our
schema, and read values out of the packet according to the value indicated in the schema. Unfortunately, konami
decided all data should be aligned very specifically, and that gaps left during alignment should be backfilled
later. This makes both reading and writing somewhat more complicated, however the system can be fairly easily
understood.</p>
<p>Firstly, we divide the payload up into 4 byte chunks. Each chunk can be allocated to either store individual
bytes, shorts, or ints (these are the buckets in the table above). When reading or writing a value, we first
check if a chunk allocated to the desired type's bucket is available and has free/as-yet-unread space within it.
If so, we will store/read our data to/from there. If there is no such chunk, we claim the next unclaimed chunk
for our bucket.</p>
<p>For example, imagine we write the sequence <code>byte, int, byte, short, byte, int, short</code>. The final output
should look like:</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>byte</td>
<td>byte</td>
<td>byte</td>
<td></td>
<td colspan="4">int</td>
<td colspan="2">short</td>
<td colspan="2">short</td>
<td colspan="4">int</td>
</tr>
</table>
<p>While this might seem a silly system compared to just not aligning values, it is at least possible to intuit that it
helps reduce wasted space. It should be noted that any variable-length structure, such as a string or an array,
claims all chunks it encroaches on for the <code>int</code> bucket, disallowing the storage of bytes or shorts
within them.</p>
<details>
<summary>Implementing a packer</summary>
<p>While the intuitive way to understand the packing algorithm is via chunks and buckets, a far more efficient
implementation can be made that uses three pointers. Rather than try to explain in words, hopefully this python
implementation should suffice as explanation:
<pre><code>class Packer:
def __init__(self, offset=0):
self._word_cursor = offset
self._short_cursor = offset
self._byte_cursor = offset
self._boundary = offset % 4
def _next_block(self):
self._word_cursor += 4
return self._word_cursor - 4
def request_allocation(self, size):
if size == 0:
return self._word_cursor
elif size == 1:
if self._byte_cursor % 4 == self._boundary:
self._byte_cursor = self._next_block() + 1
else:
self._byte_cursor += 1
return self._byte_cursor - 1
elif size == 2:
if self._short_cursor % 4 == self._boundary:
self._short_cursor = self._next_block() + 2
else:
self._short_cursor += 2
return self._short_cursor - 2
else:
old_cursor = self._word_cursor
for _ in range(math.ceil(size / 4)):
self._word_cursor += 4
return old_cursor
def notify_skipped(self, no_bytes):
for _ in range(math.ceil(no_bytes / 4)):
self.request_allocation(4)</code></pre>
</p>
</details>
<a href="./transport.html">Prev page</a> | <a href="./protocol.html">Next page</a>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block body %}
<h1><code>apsmanager</code></h1>
<h2 id="getstat"><code>apsmanager.getstat</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;apsmanager method="getstat" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;apsmanager status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,212 @@
{% extends "base.html" %}
{% block body %}
<h1><code>cardmng</code></h1>
<p>As the name might imply, this service is responsible for handling interactions with physical e-Amusement cards.
e-Amusement currently has two different types of cards in circulation. There are classic e-Amusement cards
making use of a magnetic stripe, and the newer RFID cards using FeliCa (these are probably what you have). They
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. 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>
<thead>
<tr>
<td><code>cardtype</code></td>
<td>Meaning</td>
</tr>
</thead>
<tr>
<td><code>1</code></td>
<td>Old style magnetic stripe card</td>
</tr>
<tr>
<td><code>2</code></td>
<td>FeliCa RFID card</td>
</tr>
</table>
<ul>
<li><code><a href="#inquire">cardmng.inquire</a></code></li>
<li><code><a href="#getrefid">cardmng.getrefid</a></code></li>
<li><code><a href="#bindmodel">cardmng.bindmodel</a></code></li>
<li><code><a href="#bindcard">cardmng.bindcard</a></code></li>
<li><code><a href="#authpass">cardmng.authpass</a></code></li>
<li><code><a href="#getkeepspan">cardmng.getkeepspan</a></code></li>
<li><code><a href="#getkeepremain">cardmng.getkeepremain</a></code></li>
<li><code><a href="#getdatalist">cardmng.getdatalist</a></code></li>
</ul>
<h2 id="inquire"><code>cardmng.inquire</code></h2>
<p>Request information about a card that has been inserted or touched against a reader.</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="inquire" cardid="" cardtype="" update="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>update</code></td>
<td>Should the tracked last play time be updated by this inquiry? (Just a guess)</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" refid="" dataid="" pcode="" newflag="" binded="" expired=" ecflag="" useridflag="" extidflag="" lastupdate="" /&gt;
&lt;/response&gt;</code></pre>
<p>To handle this request, we first must lookup if this <code>cardid</code> has ever been seen by our servers
before. If not, we abort with a <code>112</code> status. Otherwise, we proceeed to check if this card has been
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>
<tr>
<td><code>refid</code></td>
<td>A reference to this card to be used in other requests</td>
</tr>
<tr>
<td><code>dataid</code></td>
<td>Appears to be set the same as <code>refid</code>; presumably to allow different keys for game state vs
login details.</td>
</tr>
<tr>
<td><code>newflag</code></td>
<td>Inverse of <code>binded</code></td>
</tr>
<tr>
<td><code>binded</code></td>
<td>Has a profile ever been created for this game (or an older version, requiring a migration)
(<code>1</code> or <code>0</code>)</td>
</tr>
<tr>
<td><code>expired</code></td>
<td>? Just set to <code>0</code>.</td>
</tr>
</table>
<h2 id="getrefid"><code>cardmng.getrefid</code></h2>
<p>Register a new card to this server.</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getrefid" cardtype="" cardid=" newflag="" passwd="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>newflag</code></td>
<td>?</td>
</tr>
<tr>
<td><code>passwd</code></td>
<td>The pin for this new user. <i>Should</i> always be a four digit number (and that's worth validating),
but it's passed as a string so could feasibly be anything desired.</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" refid="" dataid="" pcode="" /&gt;
&lt;/response&gt;</code></pre>
<table>
<tr>
<td><code>refid</code></td>
<td>A reference to this card to be used in other requests</td>
</tr>
<tr>
<td><code>dataid</code></td>
<td>Appears to be set the same as <code>refid</code>; presumably to allow different keys for game state vs
login details.</td>
</tr>
<tr>
<td><code>pcode</code></td>
<td>? Not present in captured data.</td>
</tr>
</table>
<h2 id="bindmodel"><code>cardmng.bindmodel</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="bindmodel" refid="" newflag="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" dataid="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="bindcard"><code>cardmng.bindcard</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="bindcard" cardtype="" newid="" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="authpass"><code>cardmng.authpass</code></h2>
<p>Test a pin for a card. This request notably uses the <code>refid</code>, so required a
<code>cardmng.inquire</code> call to be made first.
</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="authpass" refid="" pass="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>refid</code></td>
<td>The reference we received either during <code>cardmng.inquire</code> or <code>cardmng.getrefid</code>
(the latter for new cards)</td>
</tr>
<tr>
<td><code>pass</code></td>
<td>The pin to test. See <code>cardmng.getrefid</code>.</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<p>If the pin is valid, status should be <code>0</code>. Otherwise, <code>116</code>.</p>
<h2 id="getkeepspan"><code>cardmng.getkeepspan</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getkeepspan" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" keepspan="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getkeepremain"><code>cardmng.getkeepremain</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getkeepremain" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" keepremain="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getdatalist"><code>cardmng.getdatalist</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getdatalist" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>"&gt;
&lt;item[]&gt;
&lt;mcode __type="str" /&gt;
&lt;dataid __type="str" /&gt;
&lt;regtime __type="str" /&gt;
&lt;lasttime __type="str" /&gt;
&lt;exptime __type="str" /&gt;
&lt;expflag __type="u8" /&gt;
&lt;/item[]&gt;
&lt;/cardmng&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends "base.html" %}
{% block body %}
<h1><code>dlstatus</code></h1>
<h2 id="done"><code>dlstatus.done</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;dlstatus method="done"&gt;
&lt;url&gt;
&lt;param __type="str" /&gt;
&lt;/url&gt;
&lt;name __type="str" /&gt;
&lt;size __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;dlstatus status="<i>status</i>"&gt;
&lt;progress __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/response&gt;</code></pre>
<h2 id="progress"><code>dlstatus.progress</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;dlstatus method="progress" /&gt;
&lt;progress __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;dlstatus status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -1,29 +1,9 @@
<!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><code>eacoin</code></h1>
<h2 id="checkin"><code>eacoin.checkin</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
{% extends "base.html" %}
{% block body %}
<h1><code>eacoin</code></h1>
<h2 id="checkin"><code>eacoin.checkin</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="checkin"&gt;
&lt;cardtype __type="str" /&gt;
&lt;cardid __type="str" /&gt;
@ -31,8 +11,8 @@
&lt;ectype __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>"&gt;
&lt;sequence __type="s16" /&gt;
&lt;acstatus __type="u8" /&gt;
@ -43,21 +23,21 @@
&lt;/eacoin&gt;
&lt;/response&gt;</code></pre>
<h2 id="checkout"><code>eacoin.checkout</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="checkout"><code>eacoin.checkout</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="checkout"&gt;
&lt;sessid __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="consume"><code>eacoin.consume</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="consume"><code>eacoin.consume</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="consume" esid=""&gt;
&lt;sessid __type="str" /&gt;
&lt;sequence __type="s16" /&gt;
@ -67,8 +47,8 @@
&lt;detail __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>"&gt;
&lt;acstatus __type="u8" /&gt;
&lt;autocharge __type="u8" /&gt;
@ -76,88 +56,88 @@
&lt;/eacoin&gt;
&lt;/response&gt;</code></pre>
<h2 id="getbalance"><code>eacoin.getbalance</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="getbalance"><code>eacoin.getbalance</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="getbalance"&gt;
&lt;sessid __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>"&gt;
&lt;acstatus __type="u8" /&gt;
&lt;balance __type="s32" /&gt;
&lt;/eacoin&gt;
&lt;/response&gt;</code></pre>
<h2 id="getecstatus"><code>eacoin.getecstatus</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="getecstatus"><code>eacoin.getecstatus</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="getecstatus" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>"&gt;
&lt;ectype __type="str" /&gt;
&lt;ecstatus __type="u8" /&gt;
&lt;/eacoin&gt;
&lt;/response&gt;</code></pre>
<h2 id="touch"><code>eacoin.touch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="touch"><code>eacoin.touch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="touch"&gt;
&lt;sessid __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="opchpass"><code>eacoin.opchpass</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="opchpass"><code>eacoin.opchpass</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="opchpass"&gt;
&lt;passwd __type="str" /&gt;
&lt;newpasswd __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="opcheckin"><code>eacoin.opcheckin</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="opcheckin"><code>eacoin.opcheckin</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="opcheckin"&gt;
&lt;passwd __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>"&gt;
&lt;sessid __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/response&gt;</code></pre>
<h2 id="opcheckout"><code>eacoin.opcheckout</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="opcheckout"><code>eacoin.opcheckout</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="opcheckout"&gt;
&lt;sessid __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getlog"><code>eacoin.getlog</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="getlog"><code>eacoin.getlog</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eacoin method="getlog"&gt;
&lt;sessid __type="str" /&gt;
&lt;logtype __type="str" /&gt;
@ -168,8 +148,8 @@
&lt;sesstype __type="str" /&gt;
&lt;/eacoin&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eacoin status="<i>status</i>"&gt;
&lt;processing __type="u8" /&gt;
&lt;topic&gt;
@ -197,4 +177,4 @@
&lt;/history&gt;
&lt;/eacoin&gt;
&lt;/response&gt;</code></pre>
</body>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block body %}
<h1><code>esign</code></h1>
<h2 id="request"><code>esign.request</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esign method="request"&gt;
<i>placeholder</i>
&lt;/esign&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esign status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/esign&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block body %}
<h1><code>esoc</code></h1>
<h2 id="read"><code>esoc.read</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esoc method="read"&gt;
&lt;senddata /&gt;
&lt;/esoc&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esoc status="<i>status</i>"&gt;
&lt;recvdata /&gt;
&lt;/esoc&gt;
&lt;/response&gt;</code></pre>
<p>Go figure.</p>
<h2 id="write"><code>esoc.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esoc method="write"&gt;
&lt;senddata /&gt;
&lt;/esoc&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esoc status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,38 @@
{% extends "base.html" %}
{% block body %}
<h1><code>eventlog</code></h1>
<h2 id="write"><code>eventlog.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eventlog method="write"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;data&gt;
&lt;eventid __type="str" /&gt;
&lt;eventorder __type="s32" /&gt;
&lt;pcbtime __type="u64" /&gt;
&lt;gamesession __type="s64" /&gt;
&lt;strdata1 __type="str" /&gt;
&lt;strdata2 __type="str" /&gt;
&lt;numdata1 __type="s64" /&gt;
&lt;numdata2 __type="s64" /&gt;
&lt;locationid __type="str" /&gt;
&lt;/data&gt;
&lt;/eventlog&gt;
&lt;/call&gt;</code></pre>
<p>Event ID list:</p>
<ul>
<li><code>G_GAMED</code></li>
<li><code>S_ERROR</code></li>
<li><code>S_PWRON</code> <b>TODO: find more!</b></li>
<li><code>T_OTDEMO</code></li>
</ul>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eventlog status="<i>status</i>"&gt;
&lt;gamesession __type="s64" /&gt;
&lt;logsendflg __type="s32" /&gt;
&lt;logerrlevel __type="s32" /&gt;
&lt;evtidnosendflg __type="s32" /&gt;
&lt;/eventlog&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -1,33 +1,13 @@
<!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><code>facility</code></h1>
<h2 id="get"><code>facility.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
{% extends "base.html" %}
{% block body %}
<h1><code>facility</code></h1>
<h2 id="get"><code>facility.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;facility method="get" privateip*="" encoding*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;facility expire=""\ status="<i>status</i>"&gt;
&lt;calendar*&gt;
&lt;year __type="s16" /&gt;
@ -83,58 +63,58 @@
&lt;/share&gt;
&lt;/facility&gt;
&lt;/response&gt;</code></pre>
<p><i>I'm not totally sure what type <code>share/eapass/valid</code> is meant to be, but it's optional, so I'd
suggest just not bothering and leaving it out :).</i></p>
<table>
<thead>
<tr>
<td>Country</td>
<td>Code</td>
</tr>
</thead>
<p><i>I'm not totally sure what type <code>share/eapass/valid</code> is meant to be, but it's optional, so I'd
suggest just not bothering and leaving it out :).</i></p>
<table>
<thead>
<tr>
<td>Hong Kong</td>
<td><code>HK</code></td>
<td>Country</td>
<td>Code</td>
</tr>
<tr>
<td>Taiwan</td>
<td><code>TW</code></td>
</tr>
<tr>
<td>Korea</td>
<td><code>KR</code></td>
</tr>
<tr>
<td>USA</td>
<td><code>US</code></td>
</tr>
<tr>
<td>Thailand</td>
<td><code>TH</code></td>
</tr>
<tr>
<td>Indonesia</td>
<td><code>ID</code></td>
</tr>
<tr>
<td>Singapore</td>
<td><code>SG</code></td>
</tr>
<tr>
<td>Phillipines</td>
<td><code>PH</code></td>
</tr>
<tr>
<td>Macao</td>
<td><code>MO</code></td>
</tr>
<tr>
<td>Japan</td>
<td><code>JP</code></td>
</tr>
</table>
<p><code>globalip</code> (and associated ports) shold be the IP:port of the cabinet.</p>
<p><code>region</code> is used for Japan, and has the value <code>JP-[prefecture]</code> where prefecture ranges
from 1 through 47.</p>
<p><b>TODO: Compile the list of regions</b></p>
</body>
</thead>
<tr>
<td>Hong Kong</td>
<td><code>HK</code></td>
</tr>
<tr>
<td>Taiwan</td>
<td><code>TW</code></td>
</tr>
<tr>
<td>Korea</td>
<td><code>KR</code></td>
</tr>
<tr>
<td>USA</td>
<td><code>US</code></td>
</tr>
<tr>
<td>Thailand</td>
<td><code>TH</code></td>
</tr>
<tr>
<td>Indonesia</td>
<td><code>ID</code></td>
</tr>
<tr>
<td>Singapore</td>
<td><code>SG</code></td>
</tr>
<tr>
<td>Phillipines</td>
<td><code>PH</code></td>
</tr>
<tr>
<td>Macao</td>
<td><code>MO</code></td>
</tr>
<tr>
<td>Japan</td>
<td><code>JP</code></td>
</tr>
</table>
<p><code>globalip</code> (and associated ports) shold be the IP:port of the cabinet.</p>
<p><code>region</code> is used for Japan, and has the value <code>JP-[prefecture]</code> where prefecture ranges
from 1 through 47.</p>
<p><b>TODO: Compile the list of regions</b></p>
{% endblock %}

View File

@ -1,387 +1,367 @@
<!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 id="game"><code>game</code></h1>
<h2 id="sample"><code>game.sv4_sample</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
{% extends "base.html" %}
{% block body %}
<h1 id="game"><code>game</code></h1>
<h2 id="sample"><code>game.sv4_sample</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_sample"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="new"><code>game.sv4_new</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="new"><code>game.sv4_new</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_new"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="load"><code>game.sv4_load</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="load"><code>game.sv4_load</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_load"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="load_m"><code>game.sv4_load_m</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="load_m"><code>game.sv4_load_m</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_load_m"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="save"><code>game.sv4_save</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="save"><code>game.sv4_save</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_save"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="save_m"><code>game.sv4_save_m</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="save_m"><code>game.sv4_save_m</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_save_m"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="common"><code>game.sv4_common</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="common"><code>game.sv4_common</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_common"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="shop"><code>game.sv4_shop</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="shop"><code>game.sv4_shop</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_shop"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="hiscore"><code>game.sv4_hiscore</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="hiscore"><code>game.sv4_hiscore</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_hiscore"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="buy"><code>game.sv4_buy</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="buy"><code>game.sv4_buy</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_buy"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="exception"><code>game.sv4_exception</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="exception"><code>game.sv4_exception</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_exception"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="entry_s"><code>game.sv4_entry_s</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="entry_s"><code>game.sv4_entry_s</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_entry_s"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="entry_e"><code>game.sv4_entry_e</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="entry_e"><code>game.sv4_entry_e</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_entry_e"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="frozen"><code>game.sv4_frozen</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="frozen"><code>game.sv4_frozen</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_frozen"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="lounce"><code>game.sv4_lounce</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_lounce"&gt;
<h2 id="lounge"><code>game.sv4_lounge</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_lounge"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="save_e"><code>game.sv4_save_e</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="save_e"><code>game.sv4_save_e</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_save_e"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="save_pb"><code>game.sv4_save_pb</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="save_pb"><code>game.sv4_save_pb</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_save_pb"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="save_c"><code>game.sv4_save_c</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="save_c"><code>game.sv4_save_c</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_save_c"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="play_s"><code>game.sv4_play_s</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="play_s"><code>game.sv4_play_s</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_play_s"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="play_e"><code>game.sv4_play_e</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="play_e"><code>game.sv4_play_e</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_play_e"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="serial"><code>game.sv4_serial</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="serial"><code>game.sv4_serial</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_serial"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="save_fi"><code>game.sv4_save_fi</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="save_fi"><code>game.sv4_save_fi</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_save_fi"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="print"><code>game.sv4_print</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="print"><code>game.sv4_print</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_print"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="print_h"><code>game.sv4_print_h</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="print_h"><code>game.sv4_print_h</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_print_h"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="load_r"><code>game.sv4_load_r</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="load_r"><code>game.sv4_load_r</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_load_r"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
<h2 id="save_campaign"><code>game.sv4_save_campaign</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="save_campaign"><code>game.sv4_save_campaign</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_save_campaign"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;game status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/game&gt;
&lt;/response&gt;</code></pre>
</body>
{% endblock %}

View File

@ -1,29 +1,9 @@
<!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><code>matching</code></h1>
<h2 id="request"><code>matching.request</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
{% extends "base.html" %}
{% block body %}
<h1><code>matching</code></h1>
<h2 id="request"><code>matching.request</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;matching method="request"&gt;
&lt;info&gt;
&lt;version __type="s32" /&gt;
@ -46,8 +26,8 @@
&lt;/data&gt;
&lt;/matching&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;matching status="<i>status</i>"&gt;
&lt;hostid __type="s64" /&gt;
&lt;result __type="s32" /&gt;
@ -58,9 +38,9 @@
&lt;/matching&gt;
&lt;/response&gt;</code></pre>
<h2 id="wait"><code>matching.wait</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="wait"><code>matching.wait</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;matching method="wait"&gt;
&lt;info&gt;
&lt;version __type="s32" /&gt;
@ -72,17 +52,17 @@
&lt;/data&gt;
&lt;/matching&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;matching status="<i>status</i>"&gt;
&lt;result __type="s32" /&gt;
&lt;prwtime __type="s32" /&gt;
&lt;/matching&gt;
&lt;/response&gt;</code></pre>
<h2 id="finish"><code>matching.finish</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="finish"><code>matching.finish</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;matching method="finish"&gt;
&lt;info&gt;
&lt;version __type="s32" /&gt;
@ -94,10 +74,10 @@
&lt;/data&gt;
&lt;/matching&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;matching status="<i>status</i>"&gt;
&lt;result __type="s32" /&gt;
&lt;/matching&gt;
&lt;/response&gt;</code></pre>
</body>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block body %}
<h1><code>message</code></h1>
<h2 id="get"><code>message.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;message method="get" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;message expire="" status="<i>status</i>"&gt;
&lt;item[] name="" start="" end="" data="" /&gt;
&lt;/message&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block body %}
<h1><code>package</code></h1>
<h2 id="list"><code>package.list</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;package method="list" pkgtype="<i>pkgtype</i>" model*="" /&gt;
&lt;/call&gt;</code></pre>
<p><code>all</code> is the only currently observed value for <code>pkgtype</code></p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;package status="<i>status</i>"&gt;
&lt;item[] url="" /&gt;
&lt;/package&gt;
&lt;/response&gt;</code></pre>
<p>A list of all packages available for download.</p>
<h2 id="intend"><code>package.intend</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;package method="intend" url="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;package status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block body %}
<h1><code>pcbevent</code></h1>
<h2 id="put"><code>pcbevent.put</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;pcbevent method="put"&gt;
&lt;time __type="time" /&gt;
&lt;seq __type="u32" /&gt;
&lt;item[]&gt;
&lt;name __type="str" /&gt;
&lt;value __type="s32" /&gt;
&lt;time __type="time" /&gt;
&lt;/item[]&gt;
&lt;/pcbevent&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;pcbevent status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block body %}
<h1><code>pcbtracker</code></h1>
<h2 id="alive"><code>pcbtracker.alive</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;pcbtracker method="alive" model*="" hardid="" softid="" accountid="" agree="" ecflag="" /&gt;
&lt;/call&gt;</code></pre>
<p><code>ecflag</code> here is determining if the arcade operator allows the use of paseli on this machine.</p>
<p><code>agree@</code> and <code>ecflag@</code> appear to either be totally non present, or present with a value of
<code>"1"</code>, but then again I may be reading the code wrong, so take that with a pinch of salt.
</p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;pcbtracker status="" time="" limit="" ecenable="" eclimit="" &gt;
&lt;/response&gt;</code></pre>
<p>As you might guess, <code>ecenable@</code> is therefore the flag to determine if paseli is enabled (i.e. the
arcade operator and the server both allow its use).</p>
{% endblock %}

View File

@ -1,29 +1,9 @@
<!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><code>playerdata</code></h1>
<h2 id="usergamedata_send"><code>playerdata.usergamedata_send</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
{% extends "base.html" %}
{% block body %}
<h1><code>playerdata</code></h1>
<h2 id="usergamedata_send"><code>playerdata.usergamedata_send</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;playerdata method="usergamedata_send"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;info&gt;
@ -40,16 +20,16 @@
&lt;/data&gt;
&lt;/playerdata&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;playerdata status="<i>status</i>"&gt;
&lt;result __type="s32" /&gt;
&lt;/playerdata&gt;
&lt;/response&gt;</code></pre>
<h2 id="usergamedata_recv"><code>playerdata.usergamedata_recv</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="usergamedata_recv"><code>playerdata.usergamedata_recv</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;playerdata method="usergamedata_recv"&gt;
&lt;info&gt;
&lt;version __type="u32" /&gt;
@ -62,7 +42,7 @@
&lt;/data&gt;
&lt;/playerdata&gt;
&lt;/call&gt;</code></pre>
<pre><code>&lt;call <i>...</i>&gt;
<pre><code>&lt;call <i>...</i>&gt;
&lt;playerdata method="usergamedata_recv"&gt;
&lt;data&gt;
&lt;refid __type="str"&gt;
@ -72,8 +52,8 @@
&lt;/data&gt;
&lt;/playerdata&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;playerdata status="<i>status</i>"&gt;
&lt;player&gt;
&lt;result&gt;
@ -88,12 +68,12 @@
&lt;/playerdata&gt;
&lt;/response&gt;</code></pre>
<h2 id="usergamedata_inheritance"><code>playerdata.usergamedata_inheritance</code></h2>
<p>See: <code>playerdata.usergamedata_recv</code></p>
<h2 id="usergamedata_inheritance"><code>playerdata.usergamedata_inheritance</code></h2>
<p>See: <code>playerdata.usergamedata_recv</code></p>
<h2 id="usergamedata_condrecv"><code>playerdata.usergamedata_condrecv</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="usergamedata_condrecv"><code>playerdata.usergamedata_condrecv</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;playerdata method="usergamedata_condrecv"&gt;
&lt;info&gt;
&lt;version __type="s32" /&gt;
@ -112,8 +92,8 @@
&lt;/info&gt;
&lt;/playerdata&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;playerdata status="<i>status</i>"&gt;
&lt;player&gt;
&lt;result __type="s32" /&gt;
@ -127,9 +107,9 @@
&lt;/playerdata&gt;
&lt;/response&gt;</code></pre>
<h2 id="usergamedata_scorerank"><code>playerdata.usergamedata_scorerank</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="usergamedata_scorerank"><code>playerdata.usergamedata_scorerank</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;playerdata method="usergamedata_scorerank"&gt;
&lt;info&gt;
&lt;version __type="s32" /&gt;
@ -143,8 +123,8 @@
&lt;/data&gt;
&lt;/playerdata&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;playerdata status="<i>status</i>"&gt;
&lt;rank&gt;
&lt;result __type="s32" /&gt;
@ -153,4 +133,4 @@
&lt;/rank&gt;
&lt;/playerdata&gt;
&lt;/response&gt;</code></pre>
</body>
{% endblock %}

View File

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% block body %}
<h1><code>services</code></h1>
<h2 id="get"><code>services.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;services method="get" model*="" &gt;
&lt;info&gt;
&lt;AVS2 __type="str"&gt;<i>AVS2 version</i>&lt;/AVS2&gt;
&lt;/info&gt;
&lt;/services&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;services expire="" method="get" mode="" status="<i>status</i>"&gt;
&lt;item[] name="<i>service</i>" url="<i>url</i>" /&gt;
&lt;/services&gt;
&lt;/response&gt;</code></pre>
<p>Known services are:</p>
<ul>
<li><code>ntp</code></li>
<li><code>keepalive</code></li>
<li><code>cardmng</code></li>
<li><code>facility</code></li>
<li><code>message</code></li>
<li><code>numbering</code></li>
<li><code>package</code></li>
<li><code>pcbevent</code></li>
<li><code>pcbtracker</code></li>
<li><code>pkglist</code></li>
<li><code>posevent</code></li>
<li><code>userdata</code></li>
<li><code>userid</code></li>
<li><code>eacoin</code></li>
<li><code>local</code></li>
<li><code>local2</code></li>
<li><code>lobby</code></li>
<li><code>lobby2</code></li>
<li><code>dlstatus</code></li>
<li><code>netlog</code></li>
<li><code>sidmgr</code></li>
<li><code>globby</code></li>
</ul>
<p>Most of these will usually just return the URL to the eAmuse server (or your fake one ;D). <code>ntp</code> is a
notable exception, unless you're planning on reimplementing NTP. <code>keepalive</code> will likely alsop be a
custom URL with query parameters pre-baked.</p>
<p><code>mode</code> is one of <code>operation</code>, <code>debug</code>, <code>test</code>, or
<code>factory</code>.
</p>
{% endblock %}

View File

@ -0,0 +1,74 @@
{% extends "base.html" %}
{% block body %}
<h1><code>sidmgr</code></h1>
<h2 id="create"><code>sidmgr.create</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="create"&gt;
&lt;cardtype __type="str" /&gt;
&lt;cardid __type="str" /&gt;
&lt;cardgid __type="str" /&gt;
&lt;steal __type="u8" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>"&gt;
&lt;state __type="u32" /&gt;
&lt;e_count __type="u8" /&gt;
&lt;last __type="time" /&gt;
&lt;locked __type="time" /&gt;
&lt;sid __type="str" /&gt;
&lt;cardid_status __type="u8" /&gt;
&lt;refid __type="str" /&gt;
&lt;/sidmgr&gt;
&lt;/response&gt;</code></pre>
<h2 id="open"><code>sidmgr.open</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="open" sid="" &gt;
&lt;pass __type="str" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>"&gt;
&lt;state __type="u32" /&gt;
&lt;refid __type="str" /&gt;
&lt;locked __type="time" /&gt;
&lt;/sidmgr&gt;
&lt;/response&gt;</code></pre>
<h2 id="touch"><code>sidmgr.touch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="touch" sid="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="branch"><code>sidmgr.branch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="branch" sid="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="close"><code>sidmgr.close</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="close" sid="" /&gt;
&lt;cause __type="u32" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -1,29 +1,9 @@
<!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><code>system</code></h1>
<h2 id="getmaster"><code>system.getmaster</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
{% extends "base.html" %}
{% block body %}
<h1><code>system</code></h1>
<h2 id="getmaster"><code>system.getmaster</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;system method="getmaster"&gt;
&lt;data&gt;
&lt;gamekind __type="str" /&gt;
@ -32,8 +12,8 @@
&lt;/data&gt;
&lt;/system&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;system status="<i>status</i>"&gt;
&lt;result __type="s32" /&gt;
&lt;strdata1 __type="str" /&gt;
@ -42,9 +22,9 @@
&lt;/system&gt;
&lt;/response&gt;</code></pre>
<h2 id="getlocationiplist"><code>system.getlocationiplist</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="getlocationiplist"><code>system.getlocationiplist</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;system method="getlocationiplist"&gt;
&lt;data&gt;
&lt;locationid __type="str" /&gt;
@ -52,8 +32,8 @@
&lt;/data&gt;
&lt;/system&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;system status="<i>status</i>"&gt;
&lt;result __type="s32" /&gt;
&lt;iplist&gt;
@ -65,9 +45,9 @@
&lt;/system&gt;
&lt;/response&gt;</code></pre>
<h2 id="xrpcproxy"><code>system.xrpcproxy</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="xrpcproxy"><code>system.xrpcproxy</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;system method="xrpcproxy"&gt;
&lt;info&gt;
&lt;version __type="s32" /&gt;
@ -79,8 +59,8 @@
&lt;/data&gt;
&lt;/system&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;system status="<i>status</i>"&gt;
&lt;result __type="s32" /&gt;
&lt;pwrtime __type="s32" /&gt;
@ -101,9 +81,9 @@
&lt;/system&gt;
&lt;/response&gt;</code></pre>
<h2 id="convcardnumber"><code>system.convcardnumber</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
<h2 id="convcardnumber"><code>system.convcardnumber</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;system method="convcardnumber"&gt;
&lt;info&gt;
&lt;version __type="s32" /&gt;
@ -114,8 +94,8 @@
&lt;/data&gt;
&lt;/system&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;system status="<i>status</i>"&gt;
&lt;result __type="s32" /&gt;
&lt;data&gt;
@ -123,4 +103,4 @@
&lt;/data&gt;
&lt;/system&gt;
&lt;/response&gt;</code></pre>
</body>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block body %}
<h1><code>traceroute</code></h1>
<h2 id="send"><code>traceroute.send</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;traceroute proto="" method="send"&gt;
&lt;hop[]&gt;
&lt;valid __type="bool"&gt;
&lt;addr __type="ip4"&gt;
&lt;usec __type="u64"&gt;
&lt;/hop[]&gt;
&lt;/traceroute&gt;
&lt;/call&gt;</code></pre>
<p><code>hop</code> repeats for every hop (unsurprisingly)</p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;traceroute status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block body %}
<h1><code>userdata</code></h1>
<h2 id="read"><code>userdata.read</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;userdata method="read" card*="" model*="" label="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;userdata status="<i>status</i>" time=""&gt;
&lt;b[] __type="" /&gt;
&lt;/userdata&gt;
&lt;/response&gt;</code></pre>
<p><code>__type</code> here can be either <code>bin</code> or <code>str</code></p>
<h2 id="write"><code>userdata.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;userdata method="write" card="" time="" model*="" label*="" &gt;
&lt;b[] __type="str" /&gt;
&lt;/userdata&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;userdata status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,263 @@
{% extends "base.html" %}
{% block body %}
<h1>Application Protocol</h1>
<p>As the previous pages have eluded to (you <i>did</i> read them, didn't you?), eAmuse uses HTTP as its main way of
getting data around. This means we need an HTTP server running but, as we'll see, we don't need to think too
hard about that.</p>
<p>Every request made is a <code>POST</code> request, to <code>//&lt;model&gt;/&lt;module&gt;/&lt;method&gt;</code>,
with its body being encoded data as described in the previous sections. In addition to the
<code>X-Compress:</code> and <code>X-Eamuse-Info:</code> headers previously detailed, there is also a
<code>X-PCB-ID:</code> header. that can be set. Your machine's PCB ID uniquely defines the physical board. This
header is added in out-bound requests, and allows the server to identify you. Importantly, it's also the value
that the server uses to identify which machines are authorized to be on the network, and which are not.
</p>
<p>Every request is followed immediately by a response. Any response code other than <code>200</code> is considered
a failure.</p>
<details>
<summary>Source code details</summary>
<figure>
<img src="images/200_only.png">
<figcaption><code>libavs-win32-ea3.dll:0x1000f8e7</code></figcaption>
</figure>
</details>
<p>All requests follow a basic format:</p>
<pre><code>&lt;call model="<i>model</i>" srcid="<i>srcid</i>" tag="<i>tag</i>"&gt;
&lt;<i>module</i> method="<i>method</i>" <i>...attributes</i>&gt;
<i>children</i>
&lt;/<i>module</i>&gt;
&lt;/call&gt;</code></pre>
<p>The responses follow a similar format:</p>
<pre><code>&lt;response&gt;
&lt;<i>module</i> status="<i>status</i>" <i>...attributes</i>&gt;
<i>children</i>
&lt;/<i>module</i>&gt;
&lt;/response&gt;</code></pre>
<p>With <code>"0"</code> being a successful status. Convention is to identify a specific method as
<code><i>module</i>.<i>method</i></code>, and we'll be following this convention in this document too. There are
a <i>lot</i> of possible methods, so the majority of this document is a big reference for them all. There are a
number of generic methods, and a number of game specific ones. If you haven't clocked yet, I've been working on
an SDVX 4 build for most of these pages, and each game also comes with its own set of game-specific methods.
These are namespaces under the <code>game.%s</code> module and, in the case of SDVX 4, are all
<code>game.sv4_<i>method</i></code>. I may or may not document the SDVX 4 specific methods, but I've listed them
here anyway for completeness.
</p>
<p>Paths in the XML bodies are formatted using an XPath-like syntax. That is, <code>status@/response</code> gets the
<code>status</code> attribute from <code>response</code>, and <code>response/eacoin/sequence</code> would return
that node's value.
</p>
<p><b>NOTE:</b> I am using the non-standard notation of <code>&lt;node* ...</code> and
<code>&lt;node attr*="" ...</code> to indicate that an attribute or node is not always present! Additionally, I
am going to use the notation of <code>&lt;node[]&gt;</code> to indicate that a node repeats.
</p>
<table>
<thead>
<tr>
<td>Status</td>
<td>Meaning</td>
</tr>
</thead>
<tr>
<td><code>0</code></td>
<td>Success</td>
</tr>
<tr>
<td><code>109</code></td>
<td>No profile</td>
</tr>
<tr>
<td><code>110</code></td>
<td>Not allowed</td>
</tr>
<tr>
<td><code>112</code></td>
<td>Card not found (<code>cardmng.inquire</code>)</td>
</tr>
<tr>
<td><code>116</code></td>
<td>Card pin invalid (<code>cardmng.authpass</code>)</td>
</tr>
</table>
<details>
<summary>How to reverse engineer these calls</summary>
<p>Turns out bemani have been quite sensible in how they implemented their code for creating structures, so it's
rather readable. That said, if you've been using Ghidra (like me!), this is the time to switch to IDA. I'll
let the below screenshots below speak for themselves:
</p>
<details>
<summary>Ghidra</summary>
<img src="images/eventlog_ghidra.png">
<img src="images/matching_request_ghidra.png">
</details>
<details>
<summary>IDA Pro</summary>
<img src="images/eventlog_ida.png">
<img src="images/matching_request_ida.png">
</details>
<p>I know which of these I'd rather use for reverse engineering (sorry, Ghidra)!</p>
</details>
<h2>Possible XRPC requests</h2>
<ul>
<li><code><a href="proto/eventlog.html">eventlog.%s</a></code></li>
<ul>
<li><code><a href="proto/eventlog.html#eventlog.write">eventlog.write</a></code></li>
</ul>
<li><code><a href="proto/playerdata.html">playerdata.%s</a></code></li>
<ul>
<li><code><a href="proto/playerdata.html#usergamedata_send">playerdata.usergamedata_send</a></code></li>
<li><code><a href="proto/playerdata.html#usergamedata_recv">playerdata.usergamedata_recv</a></code></li>
<li><code><a href="proto/playerdata.html#usergamedata_inheritance">playerdata.usergamedata_inheritance</a></code>
</li>
<li><code><a href="proto/playerdata.html#usergamedata_condrecv">playerdata.usergamedata_condrecv</a></code>
</li>
<li><code><a href="proto/playerdata.html#usergamedata_scorerank">playerdata.usergamedata_scorerank</a></code>
</li>
</ul>
<li><code><a href="proto/matching.html">matching.%s</a></code></li>
<ul>
<li><code><a href="proto/matching.html#request">matching.request</a></code></li>
<li><code><a href="proto/matching.html#wait">matching.wait</a></code></li>
<li><code><a href="proto/matching.html#finish">matching.finish</a></code></li>
</ul>
<li><code><a href="proto/system.html">system.%s</a></code></li>
<ul>
<li><code><a href="proto/system.html#getmaster">system.getmaster</a></code></li>
<li><code><a href="proto/system.html#getlocationiplist">system.getlocationiplist</a></code></li>
<li><code><a href="proto/system.html#xrpcproxy">system.xrpcproxy</a></code></li>
<li><code><a href="proto/system.html#convcardnumber">system.convcardnumber</a></code></li>
</ul>
<li><code><a href="proto/esoc.html">esoc.%s</a></code></li>
<ul>
<li><code><a href="proto/esoc.html#read">esoc.read</a></code></li>
<li><code><a href="proto/esoc.html#write">esoc.write</a></code></li>
</ul>
<li><code><a href="proto/cardmng.html">cardmng.%s</a></code></li>
<ul>
<li><code><a href="proto/cardmng.html#inquire">cardmng.inquire</a></code></li>
<li><code><a href="proto/cardmng.html#getrefid">cardmng.getrefid</a></code></li>
<li><code><a href="proto/cardmng.html#bindmodel">cardmng.bindmodel</a></code></li>
<li><code><a href="proto/cardmng.html#bindcard">cardmng.bindcard</a></code></li>
<li><code><a href="proto/cardmng.html#authpass">cardmng.authpass</a></code></li>
<li><code><a href="proto/cardmng.html#getkeepspan">cardmng.getkeepspan</a></code></li>
<li><code><a href="proto/cardmng.html#getkeepremain">cardmng.getkeepremain</a></code></li>
<li><code><a href="proto/cardmng.html#getdatalist">cardmng.getdatalist</a></code></li>
</ul>
<li><code><a href="proto/esign.html">esign.%s</a></code></li>
<ul>
<li><code><a href="proto/esign.html#request">esign.request</a></code></li>
</ul>
<li><code><a href="proto/package.html">package.%s</a></code></li>
<ul>
<li><code><a href="proto/package.html#list">package.list</a></code></li>
<li><code><a href="proto/package.html#intend">package.intend</a></code></li>
</ul>
<li><code><a href="proto/userdata.html">userdata.%s</a></code></li>
<ul>
<li><code><a href="proto/userdata.html#read">userdata.read</a></code></li>
<li><code><a href="proto/userdata.html#write">userdata.write</a></code></li>
</ul>
<li><code><a href="proto/services.html">services.%s</a></code></li>
<ul>
<li><code><a href="proto/services.html#get">services.get</a></code></li>
</ul>
<li><code><a href="proto/pcbtracker.html">pcbtracker.%s</a></code></li>
<ul>
<li><code><a href="proto/pcbtracker.html#alive">pcbtracker.alive</a></code></li>
</ul>
<li><code><a href="proto/pcbevent.html">pcbevent.%s</a></code></li>
<ul>
<li><code><a href="proto/pcbevent.html#put">pcbevent.put</a></code></li>
</ul>
<li><code><a href="proto/message.html">message.%s</a></code></li>
<ul>
<li><code><a href="proto/message.html#get">message.get</a></code></li>
</ul>
<li><code><a href="proto/facility.html">facility.%s</a></code></li>
<ul>
<li><code><a href="proto/facility.html#get">facility.get</a></code></li>
</ul>
<li><code><a href="proto/apsmanager.html">apsmanager.%s</a></code></li>
<ul>
<li><code><a href="proto/apsmanager.html#getstat">apsmanager.getstat</a></code></li>
</ul>
<li><code><a href="proto/sidmgr.html">sidmgr.%s</a></code></li>
<ul>
<li><code><a href="proto/sidmgr.html#create">sidmgr.create</a></code></li>
<li><code><a href="proto/sidmgr.html#open">sidmgr.open</a></code></li>
<li><code><a href="proto/sidmgr.html#touch">sidmgr.touch</a></code></li>
<li><code><a href="proto/sidmgr.html#branch">sidmgr.branch</a></code></li>
<li><code><a href="proto/sidmgr.html#close">sidmgr.close</a></code></li>
</ul>
<li><code><a href="proto/dlstatus.html">dlstatus.%s</a></code></li>
<ul>
<li><code><a href="proto/dlstatus.html#done">dlstatus.done</a></code></li>
<li><code><a href="proto/dlstatus.html#progress">dlstatus.progress</a></code></li>
</ul>
<li><code><a href="proto/eacoin.html">eacoin.%s</a></code></li>
<ul>
<li><code><a href="proto/eacoin.html#checkin">eacoin.checkin</a></code></li>
<li><code><a href="proto/eacoin.html#checkout">eacoin.checkout</a></code></li>
<li><code><a href="proto/eacoin.html#consume">eacoin.consume</a></code></li>
<li><code><a href="proto/eacoin.html#getbalance">eacoin.getbalance</a></code></li>
<li><code><a href="proto/eacoin.html#getecstatus">eacoin.getecstatus</a></code></li>
<li><code><a href="proto/eacoin.html#touch">eacoin.touch</a></code></li>
<li><code><a href="proto/eacoin.html#opchpass">eacoin.opchpass</a></code></li>
<li><code><a href="proto/eacoin.html#opcheckin">eacoin.opcheckin</a></code></li>
<li><code><a href="proto/eacoin.html#opcheckout">eacoin.opcheckout</a></code></li>
<li><code><a href="proto/eacoin.html#getlog">eacoin.getlog</a></code></li>
</ul>
<li><code><a href="proto/traceroute.html">traceroute.%s</a></code></li>
<ul>
<li><code><a href="proto/traceroute.html#send">traceroute.send</a></code></li>
</ul>
<li><code><a href="proto/game/sv4.html">game.%s</a></code></li>
<ul>
<li><code><a href="proto/game/sv4.html#sample">game.sv4_sample</a></code></li>
<li><code><a href="proto/game/sv4.html#new">game.sv4_new</a></code></li>
<li><code><a href="proto/game/sv4.html#load">game.sv4_load</a></code></li>
<li><code><a href="proto/game/sv4.html#load_m">game.sv4_load_m</a></code></li>
<li><code><a href="proto/game/sv4.html#save">game.sv4_save</a></code></li>
<li><code><a href="proto/game/sv4.html#save_m">game.sv4_save_m</a></code></li>
<li><code><a href="proto/game/sv4.html#common">game.sv4_common</a></code></li>
<li><code><a href="proto/game/sv4.html#shop">game.sv4_shop</a></code></li>
<li><code><a href="proto/game/sv4.html#hiscore">game.sv4_hiscore</a></code></li>
<li><code><a href="proto/game/sv4.html#buy">game.sv4_buy</a></code></li>
<li><code><a href="proto/game/sv4.html#exception">game.sv4_exception</a></code></li>
<li><code><a href="proto/game/sv4.html#entry_s">game.sv4_entry_s</a></code></li>
<li><code><a href="proto/game/sv4.html#entry_e">game.sv4_entry_e</a></code></li>
<li><code><a href="proto/game/sv4.html#frozen">game.sv4_frozen</a></code></li>
<li><code><a href="proto/game/sv4.html#lounge">game.sv4_lounge</a></code></li>
<li><code><a href="proto/game/sv4.html#save_e">game.sv4_save_e</a></code></li>
<li><code><a href="proto/game/sv4.html#save_pb">game.sv4_save_pb</a></code></li>
<li><code><a href="proto/game/sv4.html#save_c">game.sv4_save_c</a></code></li>
<li><code><a href="proto/game/sv4.html#play_s">game.sv4_play_s</a></code></li>
<li><code><a href="proto/game/sv4.html#play_e">game.sv4_play_e</a></code></li>
<li><code><a href="proto/game/sv4.html#serial">game.sv4_serial</a></code></li>
<li><code><a href="proto/game/sv4.html#save_fi">game.sv4_save_fi</a></code></li>
<li><code><a href="proto/game/sv4.html#print">game.sv4_print</a></code></li>
<li><code><a href="proto/game/sv4.html#print_h">game.sv4_print_h</a></code></li>
<li><code><a href="proto/game/sv4.html#load_r">game.sv4_load_r</a></code></li>
<li><code><a href="proto/game/sv4.html#save_campaign">game.sv4_save_campaign</a></code></li>
</ul>
</ul>
<b>Totally undocumented services (based on <code>services.get</code>):</b>
<ul>
<li><code>numbering</code></li>
<li><code>pkglist</code></li>
<li><code>userid</code></li>
<li><code>local</code></li>
<li><code>local2</code></li>
<li><code>lobby</code></li>
<li><code>lobby2</code></li>
<li><code>netlog</code></li>
<li><code>globby</code></li>
</ul>
<p>I'll try and figure these out in due course, promise!</p>
{% endblock %}

View File

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block body %}
<h1>Network format</h1>
<p>eAmuse packets are sent and received over HTTP (no S), with requests being in the body of <code>POST</code> requests,
and replies being in the, well, reply.</p>
<p>The packets are typically both encrypted and compressed. The compression format used is indicated by the
<code>X-Compress</code> header, and valid values are
</p>
<ul>
<li><code>none</code></li>
<li><code>lz77</code></li>
</ul>
<details>
<summary>Source code details</summary>
<figure>
<img src="./images/lz77.png">
<figcaption><code>libavs-win32-ea3.dll:0x1000fa29</code></figcaption>
</figure>
</details>
<p>Encryption is performed <b>after</b> compression, and uses RC4. RC4 is symmetric, so decryption is performed the same
as encryption. That is, <code>packet = encrypt(compress(data))</code> and
<code>data = decompress(decrypt(data))</code>.
</p>
<h2 id="keys">Encryption keys</h2>
<p>Encryption is not performed using a single static key. Instead, each request and response has its own key that is
generated.</p>
<p>These keys are generated baesd on the <code>X-Eamuse-Info</code> header.</p>
<p>This header loosely follows the format <code>1-[0-9a-f]{8}-[0-9a-f]{4}</code>. This corresponds to
<code>[version]-[serial]-[salt]</code>. <b>TODO: Confirm this</b>
</p>
<p>Our per-packet key is then generated using <code>md5(serial | salt | KEY)</code>. Identifying <code>KEY</code> is
left as an exercise for the reader, however should not be especially challenging. <span style="color: #fff">Check
the page source if you're stuck.</span></p>
<!-- It's 69d74627d985ee2187161570d08d93b12455035b6df0d8205df5, if you were wondering. libavs-win32-ea3.dll:0x10054160 -->
<h2 id="lz77">LZ77</h2>
<p>Packets are compressed using lzss. The compressed data structure is a repeating cycle of an 8 bit flags byte,
followed by 8 values. Each value is either a single literal byte, if the corresponding bit in the preceeding flag is
high, or is a two byte lookup into the window.</p>
<p>The lookup bytes are structured as <code>pppppppp ppppllll</code> where <code>p</code> is a 12 bit index in the
window, and <code>l</code> is a 4 bit integer that determines how many times to repeat the value located at that
index in the window.</p>
<p>The exact algorithm used for compression is not especially important, as long as it follows this format. One can
feasibly perform no compression at all, and instead insert <code>0xFF</code> every 8 bytes (starting at index 0), to
indicate that all values are literals. While obviously poor for compression, this is an easy way to test without
first implementing a compressor.</p>
<a href="./index.html">Prev page</a> | <a href="./packet.html">Next page</a>
{% endblock %}

View File

@ -1,54 +0,0 @@
<!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>Transport | eAmuse 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>Network format</h1>
<p>eAmuse packets are sent and received over HTTP (no S), with requests being in the body of <code>POST</code> requests, and replies being in the, well, reply.</p>
<p>The packets are typically both encrypted and compressed. The compression format used is indicated by the <code>X-Compress</code> header, and valid values are</p>
<ul>
<li><code>none</code></li>
<li><code>lz77</code></li>
</ul>
<details>
<summary>Source code details</summary>
<figure>
<img src="./images/lz77.png">
<figcaption><code>libavs-win32-ea3.dll:0x1000fa29</code></figcaption>
</figure>
</details>
<p>Encryption is performed <b>after</b> compression, and uses RC4. RC4 is symmetric, so decryption is performed the same as encryption. That is, <code>packet = encrypt(compress(data))</code> and <code>data = decompress(decrypt(data))</code>.</p>
<h2 id="keys">Encryption keys</h2>
<p>Encryption is not performed using a single static key. Instead, each request and response has its own key that is generated.</p>
<p>These keys are generated baesd on the <code>X-Eamuse-Info</code> header.</p>
<p>This header loosely follows the format <code>1-[0-9a-f]{8}-[0-9a-f]{4}</code>. This corresponds to <code>[version]-[serial]-[salt]</code>. <b>TODO: Confirm this</b></p>
<p>Our per-packet key is then generated using <code>md5(serial | salt | KEY)</code>. Identifying <code>KEY</code> is left as an exercise for the reader, however should not be especially challenging. <span style="color: #fff">Check the page source if you're stuck.</span></p>
<!-- It's 69d74627d985ee2187161570d08d93b12455035b6df0d8205df5, if you were wondering. libavs-win32-ea3.dll:0x10054160 -->
<h2 id="lz77">LZ77</h2>
<p>Packets are compressed using lzss. The compressed data structure is a repeating cycle of an 8 bit flags byte, followed by 8 values. Each value is either a single literal byte, if the corresponding bit in the preceeding flag is high, or is a two byte lookup into the window.</p>
<p>The lookup bytes are structured as <code>pppppppp ppppllll</code> where <code>p</code> is a 12 bit index in the window, and <code>l</code> is a 4 bit integer that determines how many times to repeat the value located at that index in the window.</p>
<p>The exact algorithm used for compression is not especially important, as long as it follows this format. One can feasibly perform no compression at all, and instead insert <code>0xFF</code> every 8 bytes (starting at index 0), to indicate that all values are literals. While obviously poor for compression, this is an easy way to test without first implementing a compressor.</p>
<a href="./index.html">Prev page</a> | <a href="./packet.html">Next page</a>
</body>
</html>