diff --git a/docs.py b/docs.py index 284517f..9753068 100644 --- a/docs.py +++ b/docs.py @@ -3,6 +3,8 @@ import datetime import re import os +import jinja_markdown + from flask import Flask, send_from_directory, render_template, make_response, url_for from livereload import Server @@ -12,7 +14,12 @@ import ini_lexer # NOQA: F401 app = Flask(__name__) -app.jinja_options.setdefault('extensions', []).append('jinja2_highlight.HighlightExtension') +extensions = app.jinja_options.setdefault('extensions', []) +extensions.append('jinja2_highlight.HighlightExtension') +extensions.append('jinja_markdown.MarkdownExtension') + +jinja_markdown.EXTENSIONS.append("mdx_spantables") +jinja_markdown.EXTENSIONS.append("toc") HTAG = re.compile(r"]*id=\"([^\"]+)\"[^>]*>([^<]*)" @@ -325,6 +335,7 @@ for base, _, files in os.walk(TEMPLATES + "/" + PAGES_BASE): generate_xrpc_list=generate_xrpc_list, generate_toc=lambda start=1: generate_toc(base, name, route, start), generate_footer=lambda: generate_footer(base, name, route), + relative=lambda path: os.path.join(base, path).strip("/").replace("\\", "/"), part=part, ioctl=ioctl, ) diff --git a/images/factorytest-export.png b/images/factorytest-export.png new file mode 100644 index 0000000..627f116 Binary files /dev/null and b/images/factorytest-export.png differ diff --git a/images/factorytest-sbr.png b/images/factorytest-sbr.png new file mode 100644 index 0000000..01f933b Binary files /dev/null and b/images/factorytest-sbr.png differ diff --git a/images/factorytest-tc1.png b/images/factorytest-tc1.png new file mode 100644 index 0000000..52bb343 Binary files /dev/null and b/images/factorytest-tc1.png differ diff --git a/images/factorytest-tc2.png b/images/factorytest-tc2.png new file mode 100644 index 0000000..889bc28 Binary files /dev/null and b/images/factorytest-tc2.png differ diff --git a/images/factorytest-tc3.png b/images/factorytest-tc3.png new file mode 100644 index 0000000..5467550 Binary files /dev/null and b/images/factorytest-tc3.png differ diff --git a/images/ring/PXL_20220627_191703087.jpg b/images/ring/PXL_20220627_191703087.jpg new file mode 100644 index 0000000..c41e762 Binary files /dev/null and b/images/ring/PXL_20220627_191703087.jpg differ diff --git a/images/ring/PXL_20220627_191722309.jpg b/images/ring/PXL_20220627_191722309.jpg new file mode 100644 index 0000000..3d1673f Binary files /dev/null and b/images/ring/PXL_20220627_191722309.jpg differ diff --git a/images/ring/jvs-annotated.png b/images/ring/jvs-annotated.png new file mode 100644 index 0000000..6468ac4 Binary files /dev/null and b/images/ring/jvs-annotated.png differ diff --git a/images/ring/keychip-top.jpg b/images/ring/keychip-top.jpg new file mode 100644 index 0000000..9e31e24 Binary files /dev/null and b/images/ring/keychip-top.jpg differ diff --git a/images/ring/keychip-underside.jpg b/images/ring/keychip-underside.jpg new file mode 100644 index 0000000..a75fc6b Binary files /dev/null and b/images/ring/keychip-underside.jpg differ diff --git a/mdx_spantables.py b/mdx_spantables.py new file mode 100644 index 0000000..f34d455 --- /dev/null +++ b/mdx_spantables.py @@ -0,0 +1,186 @@ +""" +SpanTables Extension for Python-Markdown +======================================== +This is a slightly modified version of the tables extension that comes with +python-markdown. +To span cells across multiple columns make sure the cells end with multiple +consecutive vertical bars. To span cells across rows fill the cell on the last +row with at least one underscore at the start or end of its content and no +other characters than spaces or underscores. +For example: + | head1 | head2 | + |-----------------|-------| + | span two cols || + | span two rows | | + |_ | | +See +for documentation of the original extension. +Original code Copyright 2009 [Waylan Limberg](http://achinghead.com) +SpanTables changes Copyright 2016 [Maurice van der Pot](griffon26@kfk4ever.com) +License: [BSD](http://www.opensource.org/licenses/bsd-license.php) +""" + +from __future__ import unicode_literals +from markdown.extensions.tables import TableProcessor +from markdown.extensions import Extension +import xml.etree.ElementTree as etree + + +class SpanTableProcessor(TableProcessor): + """ Process Tables. """ + def test(self, parent, block): + rows = block.split('\n') + return (len(rows) > 1 and '|' in rows[0] and + '|' in rows[1] and '-' in rows[1] and + rows[1].strip()[0] in ['|', ':', '-']) + + def is_end_of_rowspan(self, td): + return ((td is not None) and + (td.text.startswith('^') or td.text.endswith('^')) and + (td.text.strip('^ ') == '')) + + def apply_rowspans(self, tbody): + table_cells = {} + + rows = tbody.findall('tr') + max_cols = 0 + max_rows = len(rows) + for y, tr in enumerate(rows): + + cols = tr.findall('td') + + x = 0 + for td in cols: + + colspan_str = td.get('colspan') + colspan = int(colspan_str) if colspan_str else 1 + + # Insert the td together with its parent + table_cells[(x, y)] = (tr, td) + + x += colspan + + max_cols = max(max_cols, x) + + for x in range(max_cols): + possible_cells_in_rowspan = 0 + current_colspan = None + + for y in range(max_rows): + _, td = table_cells.get((x, y), (None, None)) + + if td is None: + possible_cells_in_rowspan = 0 + + else: + colspan = td.get('colspan') + if colspan != current_colspan: + current_colspan = colspan + possible_cells_in_rowspan = 0 + + if not td.text: + possible_cells_in_rowspan += 1 + + elif self.is_end_of_rowspan(td): + td.text = '' + possible_cells_in_rowspan += 1 + first_cell_of_rowspan_y = y - (possible_cells_in_rowspan - 1) + for del_y in range(y, first_cell_of_rowspan_y, -1): + tr, td = table_cells.get((x, del_y)) + tr.remove(td) + _, first_cell = table_cells.get((x, first_cell_of_rowspan_y)) + first_cell.set('rowspan', str(possible_cells_in_rowspan)) + + possible_cells_in_rowspan = 0 + + else: + possible_cells_in_rowspan = 1 + + def run(self, parent, blocks): + """ Parse a table block and build table. """ + block = blocks.pop(0).split('\n') + header = block[0].strip() + seperator = block[1].strip() + rows = [] if len(block) < 3 else block[2:] + # Get format type (bordered by pipes or not) + border = False + if header.startswith('|'): + border = True + # Get alignment of columns + align = [] + for c in self._split_row(seperator, border): + if c.startswith(':') and c.endswith(':'): + align.append('center') + elif c.startswith(':'): + align.append('left') + elif c.endswith(':'): + align.append('right') + else: + align.append(None) + # Build table + table = etree.SubElement(parent, 'table') + thead = etree.SubElement(table, 'thead') + self._build_row(header, thead, align, border) + tbody = etree.SubElement(table, 'tbody') + for row in rows: + self._build_row(row.strip(), tbody, align, border) + + self.apply_rowspans(tbody) + + def _build_row(self, row, parent, align, border): + """ Given a row of text, build table cells. """ + tr = etree.SubElement(parent, 'tr') + tag = 'td' + if parent.tag == 'thead': + tag = 'th' + cells = self._split_row(row, border) + c = None + # We use align here rather than cells to ensure every row + # contains the same number of columns. + for i, a in enumerate(align): + + # After this None indicates that the cell before it should span + # this column and '' indicates an cell without content + try: + text = cells[i] + if text == '': + text = None + except IndexError: # pragma: no cover + text = '' + + # No text after split indicates colspan + if text is None or text.strip() == "<": + if c is not None: + colspan_str = c.get('colspan') + colspan = int(colspan_str) if colspan_str else 1 + c.set('colspan', str(colspan + 1)) + else: + # if this is the first cell, then fall back to creating an empty cell + text = '' + else: + c = etree.SubElement(tr, tag) + c.text = text.strip() + + if a: + c.set('align', a) + + def _split_row(self, row, border): + """ split a row of text into list of cells. """ + if border: + if row.startswith('|'): + row = row[1:] + if row.endswith('|'): + row = row[:-1] + return self._split(row) + + +class TableExtension(Extension): + """ Add tables to Markdown. """ + + def extendMarkdown(self, md): + """ Add an instance of SpanTableProcessor to BlockParser. """ + md.parser.blockprocessors.register(SpanTableProcessor(md.parser, {}), 'spantable', 1000) + + +def makeExtension(*args, **kwargs): + return TableExtension(*args, **kwargs) diff --git a/static/SBSA-00.key b/static/SBSA-00.key new file mode 100644 index 0000000..d6db1b6 --- /dev/null +++ b/static/SBSA-00.key @@ -0,0 +1 @@ +ˆØ5æA3Ñ?mº ÿ”¬Ÿ \ No newline at end of file diff --git a/styles.css b/styles.css index 545c25a..c3174db 100644 --- a/styles.css +++ b/styles.css @@ -28,21 +28,29 @@ svg { transform: translateZ(0); } -td { +td, +th { border: 1px solid #111; padding: 2px; min-width: 32px; + vertical-align: top; } -table:not(.code) td { +table~table { + margin-top: 1rem; +} + +table:not(.code) td, +table:not(.code) th { padding: 2px 6px; } -table.code td { +table.code td, +table.code th { text-align: center; } -td a { +.nav a { display: block; padding: 4px 8px; } @@ -71,7 +79,8 @@ dfn { cursor: help; } -td>code { +td>code, +th>code { word-break: normal; } @@ -79,7 +88,8 @@ code>a { color: inherit; } -pre>code, .highlight { +pre>code, +.highlight { display: block; word-break: normal; border-radius: 4px; @@ -100,6 +110,12 @@ pre>.highlight { margin: 0; } +.highlight>pre>code { + border: none; + margin: 0; + padding: 0; +} + pre { max-width: 100%; overflow-x: auto; @@ -131,7 +147,8 @@ table.nav { padding-bottom: 1px; } -table.nav td { +table.nav td, +table.nav th { display: inline-block; margin-right: -1px; margin-bottom: -1px; @@ -211,7 +228,9 @@ footer>*:last-child { margin-left: 1rem; } -.part:hover>span, .part:focus>span, .part>span:hover { +.part:hover>span, +.part:focus>span, +.part>span:hover { display: block; } @@ -303,9 +322,11 @@ mark { .ata-bad { color: #f5417d; } + .ata-good { color: #4df541; } + .ata-ignore { color: #f5ad41; } @@ -321,7 +342,8 @@ mark { border-top: 1px solid #fff5; } - td { + td, + th { border: 1px solid #777; } @@ -333,11 +355,12 @@ mark { background-color: #000; } - .highlight, img:not(.graphic) { + .highlight { filter: invert(1); } - a, a:visited { + a, + a:visited { text-decoration: none; color: #f5417d } diff --git a/templates/pages/sega/hardware/keychip.html b/templates/pages/sega/hardware/keychip.html index 3eac51c..b767071 100644 --- a/templates/pages/sega/hardware/keychip.html +++ b/templates/pages/sega/hardware/keychip.html @@ -1,441 +1,3 @@ {% extends "sega.html" %} {% block title %}Ring Keychip{% endblock %} {% block body %} -

Ring Keychip

-

- The Ring keychip is arguably simultaniously one of the most overkill while least utilised parts of the system. - On-board is a PIC microcontroller, a dedicated cryptography chip, a hardware SHA engine for authentication, and - flash storage. -

-

Protocol

-

- The PIC communicates with the system using a parallel bus. This bus is exposed physically on the keychip connector, - and in software can be accessed using \\.\mxparallel. All bus communication is encrypted using AES 128 - ECB, using a different key for each data direction. Send/receive is defined from the perspective of the Ring system. - That is, the "Send" key handles data from the Ring to the keychip, and the "Receive" key handles data from the - keychip to the Ring. The initial key values are: -

-

Initial receive key:

-
75 6f 72 61 74 6e 65 6b 61 6d 69 68 73 75 6b 75
-

Initial send key:

-
66 6E 65 6B 65 72 61 77 64 72 61 68 61 67 65 73
- -

- All packets are first prefixed by a command ordinal (see below), then command-specific information. The base unit of - transfer is 16 bytes due to AES 128. Unused bytes can contain anything, however mxkeychip chooses to pad using - random bytes derrived from the current system time. -

- -

Command Ordinals

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OrdinalCommand
0SetKeyS
1SetKeyR
2SetIv
3Decrypt
4Encrypt
5GetAppBootInfo
6EepromWrite
7EepromRead
8NvramWrite
9NvramRead
10AddPlayCount
11FlashRead
12FlashErase
13
14FlashWrite
15
16
17
18
19
20KcGetVersion
21SetMainId
22GetMainId
23SetKeyId
24GetKeyId
25GetPlayCounter
- -

SetKeyS

-

Sets the "send" encryption key. The key is changed before communication of the reply.

-

Request

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
0unused
"send" encryption key
-

Response

- - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
0unused
-

SetKeyR

-

Sets the "receive" encryption key. The key is changed before communication of the reply.

-

Request

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
1unused
"receive" encryption key
-

Response

- - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
1unused
-

SetIv

-

Reset the game key IV to its initial value

-

Request

- - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
2unused
-

Response

- - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
2unused
-

Decrypt

-

Decrypt a block of data using the game key

-

Request

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
3unused
ciphertext to decrypt
-

Request

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
3unused
decrypted plaintext
-

Encrypt

-

Encrypt a block of data using the game key

-

Request

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
4unused
plaintext to encrypt
-

Request

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0123456789ABCDEF
4unused
encrypted ciphertext
-{% endblock %} +{% markdown %}{% include relative("~keychip.md") %}{% endmarkdown %} +{% endblock %} \ No newline at end of file diff --git a/templates/pages/sega/hardware/~keychip.md b/templates/pages/sega/hardware/~keychip.md new file mode 100644 index 0000000..93e87d1 --- /dev/null +++ b/templates/pages/sega/hardware/~keychip.md @@ -0,0 +1,367 @@ +# Ring Keychip + +The Ring keychip is arguably simultaniously one of the most overkill while least utilised parts of the system. On-board is a PIC microcontroller, a dedicated cryptography chip, a hardware SHA engine for authentication, and flash storage. + +## Protocol + +The PIC communicates with the system using a parallel bus. This bus is exposed physically on the keychip connector, and in software can be accessed using `\\.\mxparallel`. All bus communication is encrypted using AES 128 ECB, using a different key for each data direction. Send/receive is defined from the perspective of the Ring system. That is, the "Send" key handles data from the Ring to the keychip, and the "Receive" key handles data from the keychip to the Ring. The initial key values are: + +### Initial receive key: +``` +75 6f 72 61 74 6e 65 6b 61 6d 69 68 73 75 6b 75 +``` +### Initial send key: +``` +66 6E 65 6B 65 72 61 77 64 72 61 68 61 67 65 73 +``` + +All packets are first prefixed by a command ordinal (see below), then command-specific information. The base unit of transfer is 16 bytes due to AES 128. Unused bytes can contain anything, however mxkeychip chooses to pad using random bytes derrived from the current system time. + +## Storage locations +### EEPROM +Stores entries detailing the tracedata metadata. There is a copy of the structure at 0x000 and a duplicate at 0x100. Each structure is comprised of 16 entries, where each entry first has a 4 byte CRC, then 12 bytes currently unknown. + +### NVRAM +| Start | End | Content | +| ---------------- | ---------------- | ------------------ | +| 1800h | 1bffh | billing public key | +| 1c00h | 1fffh | ca certificate | + +Each block begins with a 4 byte integer indicating the length of the data following it (up to 1020 bytes). + +### Flash + +| Start | End | Content | +| ----------------- | ----------------- | ------------------ | +| 00000h | 0ffffh | tracedata sector 1 | +| 10000h | 1ffffh | tracedata sector 2 | +| 20000h | 2ffffh | tracedata sector 3 | +| 30000h | 3ffffh | tracedata sector 4 | +| 40000h | 4ffffh | tracedata sector 5 | +| 50000h | 5ffffh | tracedata sector 6 | +| 60000h | 6ffffh | tracedata sector 7 | +| 70000h | 70fffh | nvram0 | +| 71000h | 71fffh | nvram1 | +| 72000h | 72fffh | nvram2 | +| 73000h | 73fffh | nvram3 | +| 74000h | 74fffh | nvram4 | +| 75000h | 75fffh | nvram5 | +| 76000h | 76fffh | nvram6 | +| 77000h | 77fffh | nvram7 | +| 78000h | 78fffh | nvram8 | +| 79000h | 79fffh | nvram9 | +| 7a000h | 7a10bh | billing info | +| 7b000h | 7b10bh | billing info dup | + +Each tracedata sector begins with a 128-byte bitfield (1024 bits) indicating which blocks in the sector have been populated. Following this are 1022 blocks, 64 bytes each. (the last two of the theorhetical 1024 would overlap into the next sector). Each slot is individually encrypted with DES ECB, using key `4D77F1748D6D1094`. + +This is implemented via the `AT25DF041A` 4Mbit flash chip on the keychip PCB. The maximum address is therefore 7ffffh. + +## Command Ordinals +| Ordinal | Command | +| ------- | --------------------------------- | +| `0` | [SetKeyS](#setkeys) | +| `1` | [SetKeyR](#setkeyr) | +| `2` | [SetIv](#setiv) | +| `3` | [Decrypt](#decrypt) | +| `4` | [Encrypt](#encrypt) | +| `5` | [GetAppBootInfo](#getappbootinfo) | +| `6` | [EepromWrite](#eepromwrite) | +| `7` | [EepromRead](#eepromread) | +| `8` | [NvramWrite](#nvramwrite) | +| `9` | [NvramRead](#nvramread) | +| `10` | [AddPlayCount](#addplaycount) | +| `11` | [FlashRead](#flashread) | +| `12` | [FlashErase](#flasherase) | +| `13` | ? | +| `14` | [FlashWrite](#flashwrite) | +| `15` | ? | +| `16` | ? | +| `17` | ? | +| `18` | ? | +| `19` | ? | +| `20` | [KcGetVersion](#kcgetversion) | +| `21` | [SetMainId](#setmainid) | +| `22` | [GetMainId](#getmainid) | +| `23` | [SetKeyId](#setkeyid) | +| `24` | [GetKeyId](#getkeyid) | +| `25` | [GetPlayCounter](#getplaycounter) | + +## SetKeyS +Sets the "send" encryption key. The key is changed before communication of the reply. + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 0 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| ^ | key | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | +| Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + + +| Variable | | +| -------- | ------------ | +| key | The send key | + +## SetKeyR +Sets the "receive" encryption key. The key is changed before communication of the reply. + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 1 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| ^ | key | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | +| Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +| Variable | | +| -------- | --------------- | +| key | The receive key | + +## SetIv +Reset the game key IV to its initial value + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 2 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +## Decrypt +Decrypt a block of data using the game key + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 3 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | ct | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | +| Receive | pt | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | + +| Variable | | +| -------- | ------------------------ | +| ct | The encrypted ciptertext | +| pt | The decrypted plaintext | + +## Encrypt +Encrypt a block of data using the game key + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 4 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| ^ | pt | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | +| Receive | ct | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | + +| Variable | | +| -------- | ------------------------ | +| pt | The decrypted plaintext | +| ct | The encrypted ciptertext | + +## GetAppBootInfo +Request the AppBoot structure from the keychip. The AppBoot structure is 256 bytes, thus requires 16 packets. + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ------------------- | --- | --- | -------- | --------------- | --- | --- | --- | ------- | --- | --- | --- | ------ | ---------- | ----------- | --- | +| Send | 5 | 0 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | CRC | < | < | < | Format | < | < | < | Game ID | < | < | < | Region | Model type | System flag | ? | +| | Platform ID | < | < | DVD flag | Network address | < | < | < | | | | | | | | | +| | | | | | | | | | | | | | | | | | +| | _206 bytes padding_ | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | | | | | | | | | | | | | | | | | +| ^ | Seed | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | + +| Variable | | +| --------------- | ----------------------------------------------------- | +| CRC | CRC32 checksum of the reamining 252 bytes | +| Format | AppBoot format type. The described type is format `1` | +| Game ID | Four character game ID. For example, `SBTR` | +| Region | Region bitmask | +| Model type | Model type this keychip is for | +| System flag | System flag bitmask | +| DVD flag | Is update from DVD allowed | +| Network address | Permitted subnet. Fourth octet is null | + +**Note:** I'm not sure what the `0` is for here. Surprises await for anything other than `0` :D + +## EepromWrite + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ---- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 6 | reg | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| ^ | data | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | +| Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +| Variable | | +| -------- | --------------------------------------------------------------------- | +| reg | EEPROM register to write. EEPROM is divided into 16x16 byte registers | +| data | The data to write | + +## EepromRead + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ---- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 7 | reg | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | data | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | + +| Variable | | +| -------- | -------------------------------------------------------------------- | +| reg | EEPROM register to read. EEPROM is divided into 16x16 byte registers | +| data | The data read | + +## NvramWrite + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ---- | ---- | --- | ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 8 | addr | < | blocks | * | * | * | * | * | * | * | * | * | * | * | * | +| ^ | data | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | +| Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +| Variable | | +| -------- | ----------------------------------------------------- | +| addr | Absolute offset in NVRAM to write at | +| blocks | The number of 16 bytes blocks to write | +| data | The data to write. This will require `blocks` packets | + +## NvramRead + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ---- | ---- | --- | ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 8 | addr | < | blocks | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | data | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | + +| Variable | | +| -------- | ------------------------------------------------- | +| addr | Absolute offset in NVRAM to read at | +| blocks | The number of 16 bytes blocks to read | +| data | The read data. This will require `blocks` packets | + +## AddPlayCount +Increment the playcount value + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 10 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +## FlashRead + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ---- | --- | ---- | --- | --- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 11 | addr | < | < | # bytes | < | < | * | * | * | * | * | * | * | * | * | + +| Variable | | +| -------- | ----------------------------------- | +| addr | Absolute offset in flash to read at | +| # bytes | The number of bytes to read | + +**Important:** Data is returned as raw bytes transmitted over parallel, without encryption or packet encapsulation. + +## FlashErase + +Honestly not actually sure if this is an erase or what. Still working on it! + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --- | ---- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 12 | addr | < | < | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +## FlashWrite + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --- | ---- | --- | --- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 14 | addr | < | < | # bytes | < | < | * | * | * | * | * | * | * | * | * | +| | +| Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +| Variable | | +| -------- | ------------------------------------ | +| addr | Absolute offset in flash to write at | +| # bytes | The number of bytes to write | + +**Important:** Data is transmitted immediately following transmission of the first packet. Is it transmitted as raw bytes over parallel, without encryption or packet encapsulation. The response packet is transmitted after # bytes have been received in this manner. + +## KcGetVersion + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 20 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | version | < | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +| Variable | | +| -------- | --------------------------------------------------------------- | +| version | Two-byte version number. Keychips are observed to return 01,04. | + +## SetMainId +Set the Main ID on the keychip, if not already set. + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 21 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| ^ | Main ID | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | +| Receive | err | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +| Variable | | +| -------- | ------------------------------------------------------------------ | +| Main ID | The new keychip Main ID. No validation is performed of this value. | +| err | ? (0?) when succesful, FFh otherwise | + +## GetMainId +Get the Main ID on the keychip. + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 22 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | Main ID | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | + +| Variable | | +| -------- | ------------------- | +| Main ID | The keychip Main ID | + +## SetKeyId +Set the Key ID on the keychip, if not already set. + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 23 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| ^ | Key ID | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | +| | +| Receive | err | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | + +| Variable | | +| -------- | ------------------------------------------------------------- | +| Key ID | The new keychip ID. No validation is performed of this value. | +| err | ? (0?) when succesful, FFh otherwise | + +## GetKeyId +Get the Key ID on the keychip. + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 24 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | Key ID | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | + +| Variable | | +| -------- | -------------- | +| Key ID | The keychip ID | + +## GetPlayCounter +Get the current playcount value + +| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +| ------- | --------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Send | 24 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| | +| Receive | playcount | < | < | < | * | * | * | * | * | * | * | * | * | * | * | * | + +| Variable | | +| --------- | --------------------------- | +| playcount | The billing playcount value | diff --git a/templates/pages/sega/manual/errors.html b/templates/pages/sega/manual/errors.html index 6b16c33..86a6f81 100644 --- a/templates/pages/sega/manual/errors.html +++ b/templates/pages/sega/manual/errors.html @@ -410,26 +410,26 @@ 0800 - Unknown Error. + Unexpected Failure Generic error code. Check the event log for details ALLNetProc 0801 - Unknown Error. - Generic error code. Check the event log for details + Main board Malfunctioning + RTC access failed ALLNetProc 0802 - Unknown Error. - Generic error code. Check the event log for details + Main board Malfunctioning + File access failed ALLNetProc 0803 Unknown Error. - Generic error code. Check the event log for details + Invalid billing config file ALLNetProc @@ -665,7 +665,7 @@ 8111 ALL.Net System error(REG) - + ALL.Net rejected the keychip ALLNetProc @@ -1458,4 +1458,29 @@ + +0390 - nxAuth is not working +0391 - Not Set Valid App Property +0392 - Not Found Valid System File +0393 - Not Found Aime Device Firmware File +0700 - Wrong Coin Setting +0701 - Wrong Cabinet Setting +0702 - Unexpected SelectMenu Failure +0703 - Storage Device Malfunctioning +0704 - Storage Device Malfunctioning +0705 - Game Program Not Acceptable +0706 - Storage Device Malfunctioning +0707 - Game Program Not Found +0708 - Storage Device Malfunctioning +0912 - Unexpected Game Program Failure +0913 - Game Program Not Found on Storage Device +6501 - Aime Card Reader Not Found +6502 - Aime Card Reader Unknown Error +6503 - Aime Read failed +6504 - Illegal Card Found +6505 - Occupied Aime Found +6506 - Aime Card Reader Firmware Update failed +6507 - Aime Card Reader Initialize failed +6508 - Aime DB Access failed + {% endblock %} \ No newline at end of file diff --git a/templates/pages/sega/software/factorytest.html b/templates/pages/sega/software/factorytest.html new file mode 100644 index 0000000..023da4a --- /dev/null +++ b/templates/pages/sega/software/factorytest.html @@ -0,0 +1,3 @@ +{% extends "sega.html" %} {% block title %}Ring Series ASSY TEST{% endblock %} {% block body %} +{% markdown %}{% include relative("~factorytest.md") %}{% endmarkdown %} +{% endblock %} \ No newline at end of file diff --git a/templates/pages/sega/software/geminifs.html b/templates/pages/sega/software/geminifs.html new file mode 100644 index 0000000..90bc80a --- /dev/null +++ b/templates/pages/sega/software/geminifs.html @@ -0,0 +1,47 @@ +{% extends "sega.html" %} +{% block title %}geminifs{% endblock %} +{% block body %} +

geminifs

+

geminifs is an incredibly interesting driver for merging two volumes into a single mounted drive. It is comprised of + geminifs.exe and geminifs.sys. The former is the interface programs are expected to use, + and the latter is the driver itself.

+ +

geminifs.exe help

+

+Usage: geminifs.exe    [cache=on|off] [reparse=on|off]
+       geminifs.exe -u 
+
+ex1: mount drive
+ > geminifs.exe O:\ P:\ X:
+ > geminifs.exe O:\ P:\ X: cache=off
+ > geminifs.exe O:\ P:\ X: cache=on reparse=on
+
+ex2: dismount drive
+ > geminifs.exe -u X:
+
+  Options:
+
+    cache=on
+          Enable geminifs file-cache.
+    cache=off
+          Disable  geminifs  file-cache.   File access memory consumption is a
+        minimum, have to sacrifice access speed.
+
+    reparse=on
+          Enable the open file as a 'Reparse Point' to the original path name.
+    reparse=off
+          When you open a file  mount,  the geminifs driver open the file  and
+        read access indirectly.
+
+   The geminifs driver  that can change  the value  of these options  for each
+volume to be mounted (by using these command-line).  If no value is specified,
+each of the following registry value is used.
+
+  [Service-Key]\Parameters\FsCache              REG_DWORD  0:OFF 1:ON
+  [Service-Key]\Parameters\FsOpenFileAsReparse  REG_DWORD  0:OFF 1:ON
+
+  If these are not specified, the default value (cache=on, reparse=on) will be
+used.
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/pages/sega/software/index.html b/templates/pages/sega/software/index.html index f4bcb43..894af79 100644 --- a/templates/pages/sega/software/index.html +++ b/templates/pages/sega/software/index.html @@ -105,7 +105,7 @@ mxshellexecute.exe --> stroke="currentColor"> - s:\mxinstaller.exe -cmdport 40102 -bindport 40103 + s:\mxinstaller.exe -cmdport 40102 -binport 40103
diff --git a/templates/pages/sega/software/mx/mxstartup.html b/templates/pages/sega/software/mx/mxstartup.html index 511a7cf..b462670 100644 --- a/templates/pages/sega/software/mx/mxstartup.html +++ b/templates/pages/sega/software/mx/mxstartup.html @@ -137,4 +137,11 @@

These errors are also reported into the Application event log, under the source mxstartup.

+

TrueCrypt Commands

+
  • +
      TrueCrypt /k Z:\UpdateKeyFile /v [...] \Device\Harddisk%d\Partition%d /l W: /s /q
    +
      TrueCrypt /k Z:\UpdateKeyFile /v Z:\Minint\System32\DEFAULT_DRIVERS /l V: /s /q
    +
      TrueCrypt /p segahardpassword /k Z:\SystemKeyFile /v C:\System\Execute\System /l S: /w /s /q
    +
  • + {% endblock %} \ No newline at end of file diff --git a/templates/pages/sega/software/~factorytest.md b/templates/pages/sega/software/~factorytest.md new file mode 100644 index 0000000..4c6fa67 --- /dev/null +++ b/templates/pages/sega/software/~factorytest.md @@ -0,0 +1,51 @@ +# Ring Series ASSY TEST + +## What? +This is the testing tool used for self-checking RingWide, RingEdge and RingEdge2 PC systems. The automatic operation + of the tool is controlled by a number of configuration files stored on a locally attached USB storage device. + +## Known Versions + +| Version | Dumped? | Notes | +| ------- | ------- | ------------------------------- | +| 1.02 | No | | +| 1.04 | Yes | Only prefactorytest.exe changed | +| 1.10 | Yes | | +| 1.13 | Yes | | +| 1.16 | Yes | | + +## How can I help? + +The factory testing tool is installed into the `patch1` partition. Very few Ring games utilise this partition, and the test tool is not uninstalled for some reason, meaning we can extract it from the drive! + +To start with, we need to locate the [SEGA Boot Record]({{ROOT}}/sega/misc/partition.html) and check if we have SBSA present in `patch1`. While this could be done totally manually, it's a lot easier to use disk forensics software as it will handle partition identification for us. Pictured is FTK Imager (free), however Autopsy (free) and X-Ways (not free) are all viable options. + +![]({{ROOT}}/images/factorytest-sbr.png) + +Starting at 0x5c0, we can begin to read out the partition information. First is the game ID, `SBSA`. Following that, is the install timestamp. This is + +```c +struct { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t rsv; +} +``` + +The pictured example is `02 Apr 2010, 18:26:26`. The next four bytes are the version number. `04 00 01 00` here indicates version `1.04`. + +If the version here is marked as dumped in the above table, then thank you for checking, but there's nothing new to find here! If it's not, then congratulations you've just found a new version! Let's get to dumping it! + +The easiest way to proceed at this point is the extract the entire partition as a file. The partition we want is the second 2GiB partition. This is partition 7, however your software may have numbered it as partition 5. + +The file we have created is a TrueCrypt volume. There is no password, and the keyfile is [`88D835E64133D13F6DBAA0FF9413AC9F`]({{ROOT}}/static/SBSA-00.key) (click to download). + +![]({{ROOT}}/images/factorytest-tc1.png) +![]({{ROOT}}/images/factorytest-tc2.png) +![]({{ROOT}}/images/factorytest-tc3.png) + +The files can now be copied from the chosen drive letter. **Please share them!**