move 315-5881 based encryption to it's own file, allowing me to experiment with the device for the encrypted ST-V games.
This commit is contained in:
parent
2eb1a3a4f7
commit
f1d9ba9df8
@ -1021,6 +1021,9 @@ static MACHINE_CONFIG_START( stv, stv_state )
|
||||
MCFG_SOUND_ADD("cdda", CDDA, 0)
|
||||
MCFG_SOUND_ROUTE(0, "lspeaker", 1.0)
|
||||
MCFG_SOUND_ROUTE(1, "rspeaker", 1.0)
|
||||
|
||||
MCFG_DEVICE_ADD("315_5881", SEGA315_5881_CRYPT, 0)
|
||||
//MCFG_SET_READ_CALLBACK(stv_state, read_callback)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
/*
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "bus/generic/slot.h"
|
||||
#include "bus/generic/carts.h"
|
||||
|
||||
#include "machine/315-5881_crypt.h"
|
||||
|
||||
#define MAX_FILTERS (24)
|
||||
#define MAX_BLOCKS (200)
|
||||
#define MAX_DIR_SIZE (256*1024)
|
||||
@ -33,7 +35,8 @@ public:
|
||||
m_cart3(*this, "stv_slot3"),
|
||||
m_cart4(*this, "stv_slot4"),
|
||||
m_gfxdecode(*this, "gfxdecode"),
|
||||
m_palette(*this, "palette")
|
||||
m_palette(*this, "palette"),
|
||||
m_cryptdevice(*this, "315_5881")
|
||||
{
|
||||
}
|
||||
|
||||
@ -167,6 +170,9 @@ public:
|
||||
required_device<gfxdecode_device> m_gfxdecode;
|
||||
required_device<palette_device> m_palette;
|
||||
|
||||
optional_device<sega_315_5881_crypt_device> m_cryptdevice;
|
||||
|
||||
|
||||
bitmap_rgb32 m_tmpbitmap;
|
||||
DECLARE_VIDEO_START(stv_vdp2);
|
||||
UINT32 screen_update_saturn(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
|
||||
|
815
src/mame/machine/315-5881_crypt.c
Normal file
815
src/mame/machine/315-5881_crypt.c
Normal file
@ -0,0 +1,815 @@
|
||||
/*
|
||||
re: Tecmo World Cup '98 (ST-V) (from ANY)
|
||||
|
||||
I got one of the card in subject open it up to check the rom version
|
||||
and made a discovery...
|
||||
The protection chip has the part number on it "315-5881", it's the same
|
||||
used on naomi M2 carts as you can see here
|
||||
http://imagizer.imageshack.us/a/img540/7634/BsqvD8.jpg
|
||||
|
||||
The same chip 315-5881 but with a Lattice IspLSI2032 (Sega part
|
||||
315-6050) was used on some Model3 games...
|
||||
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "machine/315-5881_crypt.h"
|
||||
|
||||
extern const device_type SEGA315_5881_CRYPT = &device_creator<sega_315_5881_crypt_device>;
|
||||
|
||||
|
||||
sega_315_5881_crypt_device::sega_315_5881_crypt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: device_t(mconfig, SEGA315_5881_CRYPT, "Sega 'SEGA315_5881' Encryption device", tag, owner, clock, "SEGA315_5881", __FILE__)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void sega_315_5881_crypt_device::device_start()
|
||||
{
|
||||
buffer = auto_alloc_array(machine(), UINT8, BUFFER_SIZE);
|
||||
line_buffer = auto_alloc_array(machine(), UINT8, LINE_SIZE);
|
||||
line_buffer_prev = auto_alloc_array(machine(), UINT8, LINE_SIZE);
|
||||
|
||||
m_read.bind_relative_to(*owner());
|
||||
|
||||
save_pointer(NAME(buffer), BUFFER_SIZE);
|
||||
save_pointer(NAME(line_buffer), LINE_SIZE);
|
||||
save_pointer(NAME(line_buffer_prev), LINE_SIZE);
|
||||
save_item(NAME(prot_cur_address));
|
||||
save_item(NAME(subkey));
|
||||
save_item(NAME(enc_ready));
|
||||
save_item(NAME(dec_hist));
|
||||
save_item(NAME(dec_header));
|
||||
save_item(NAME(buffer_pos));
|
||||
save_item(NAME(line_buffer_pos));
|
||||
save_item(NAME(line_buffer_size));
|
||||
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::device_reset()
|
||||
{
|
||||
memset(buffer, 0, BUFFER_SIZE);
|
||||
memset(line_buffer, 0, LINE_SIZE);
|
||||
memset(line_buffer_prev, 0, LINE_SIZE);
|
||||
|
||||
prot_cur_address = 0;
|
||||
subkey = 0;
|
||||
dec_hist = 0;
|
||||
dec_header = 0;
|
||||
enc_ready = false;
|
||||
|
||||
buffer_pos = 0;
|
||||
line_buffer_pos = 0;
|
||||
line_buffer_size = 0;
|
||||
buffer_bit = 0;
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::do_decrypt(UINT8 *&base)
|
||||
{
|
||||
if(!enc_ready)
|
||||
enc_start();
|
||||
if(dec_header & FLAG_COMPRESSED) {
|
||||
if(line_buffer_pos == line_buffer_size)
|
||||
line_fill();
|
||||
base = line_buffer + line_buffer_pos;
|
||||
line_buffer_pos += 2;
|
||||
} else {
|
||||
if(buffer_pos == BUFFER_SIZE)
|
||||
enc_fill();
|
||||
base = buffer + buffer_pos;
|
||||
buffer_pos += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::set_addr_low(UINT16 data)
|
||||
{
|
||||
prot_cur_address = (prot_cur_address & 0xffff0000) | data;
|
||||
enc_ready = false;
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::set_addr_high(UINT16 data)
|
||||
{
|
||||
prot_cur_address = (prot_cur_address & 0x0000ffff) | (data << 16);
|
||||
enc_ready = false;
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::set_subkey(UINT16 data)
|
||||
{
|
||||
subkey = data;
|
||||
enc_ready = false;
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::set_key(UINT32 data)
|
||||
{
|
||||
key = data;
|
||||
enc_ready = false;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
DECRYPTION EMULATION
|
||||
|
||||
By convention, we label the three known cart protection methods this way (using Deunan Knute's wording):
|
||||
M1: DMA read of protected ROM area
|
||||
M2: special read of ROM area which supplies decryption key first
|
||||
M3: normal read followed by write to cart's decryption buffer (up to 64kB), followed by M2 but from buffer area
|
||||
|
||||
Notes below refer to M2 & M3.
|
||||
|
||||
The encryption is done by a stream cipher operating in counter mode, which use a 16-bits internal block cipher.
|
||||
|
||||
There are 2 "control bits" at the start of the decrypted stream which control the mode of operation: bit #1 set to 1 means
|
||||
that the decrypted stream needs to be decompressed after being decrypted. More on this later.
|
||||
|
||||
The next 16-bits are part of the header (they don't belong to the plaintext), but his meaning is unclear. It has been
|
||||
conjectured that it could stablish when to "reset" the process and start processing a new stream (based on some tests
|
||||
on WWFROYAL, in which the decryption's output doesn't seem to be valid for more than some dozens of words), but some
|
||||
more testing would be needed for clarifying that.
|
||||
|
||||
After those 18 heading bits, we find the proper plaintext. It must be noted that, due to the initial 2 special bits,
|
||||
the 16-bits words of the plaintext are shifted 2 bits respect to the word-boundaries of the output stream of the
|
||||
internal block-cipher. So, at a given step, the internal block cipher will output 16-bits, 14 of which will go to a
|
||||
given plaintext word, and the remaining 2 to the next plaintext word.
|
||||
|
||||
The underlying block cipher consists of two 4-round Feistel Networks (FN): the first one takes the counter (16 bits),
|
||||
the game-key (>=26 bits) and the sequence-key (16 bits) and output a middle result (16 bits) which will act as another key
|
||||
for the second one. The second FN will take the encrypted word (16 bits), the game-key, the sequence-key and the result
|
||||
from the first FN and will output the decrypted word (16 bits).
|
||||
|
||||
Each round of the Feistel Networks use four substitution sboxes, each having 6 inputs and 2 outputs. The input can be the
|
||||
XOR of at most two "sources bits", being source bits the bits from the previous round and the bits from the different keys.
|
||||
|
||||
The underlying block cipher has the same structure than the one used by the CPS-2 (Capcom Play System 2) and,
|
||||
indeed, some of the used sboxes are exactly the same and appear in the same FN/round in both systems (this is not evident,
|
||||
as you need to apply a bitswapping and some XORs to the input & output of the sboxes to get the same values due). However,
|
||||
the key scheduling used by this implementation is much weaker than the CPS-2's one. Many s-boxes inputs aren't XORed with any
|
||||
key bit.
|
||||
|
||||
Due to the small key-length, no sophisticated attacks are needed to recover the keys; a brute-force attack knowing just
|
||||
some (encrypted word-decrypted word) pairs suffice. However, due to the weak key scheduling, it should be noted that some
|
||||
related keys can produce the same output bytes for some (short) input sequences.
|
||||
|
||||
The only difference in the decryption process between M2 and M3 is the initialization of the counter. In M3, the counter is
|
||||
always set to 0 at the beginning of the decryption while, in M2, the bits #1-#16 of the ciphertext's address are used
|
||||
to initialize the counter.
|
||||
|
||||
Note that this implementation considers that the counter initialization for ram decryption is 0 simply because the ram is
|
||||
mapped to multiples of 128K.
|
||||
|
||||
Due to the nature of the cipher, there are some degrees of freedom when choosing the s-boxes and keys values; by example,
|
||||
you could apply a fixed bitswapping and XOR to the keys and the decryption would remain the same as long as you change
|
||||
accordingly the s-boxes' definitions. So the order of the bits in the keys is arbitrary, and the s-boxes values have been
|
||||
chosen so as to make the key for CAPSNK equal to 0.
|
||||
|
||||
It can be observed that a couple of sboxes have incomplete tables (a 255 value indicate an unknown value). The recovered keys
|
||||
as of december/2010 show small randomness and big correlations, making possible that some unseen bits could make the
|
||||
decryption need those incomplete parts.
|
||||
|
||||
****************************************************************************************/
|
||||
|
||||
const sega_315_5881_crypt_device::sbox sega_315_5881_crypt_device::fn1_sboxes[4][4] = {
|
||||
{ // 1st round
|
||||
{
|
||||
{
|
||||
0,3,2,2,1,3,1,2,3,2,1,2,1,2,3,1,3,2,2,0,2,1,3,0,0,3,2,3,2,1,2,0,
|
||||
2,3,1,1,2,2,1,1,1,0,2,3,3,0,2,1,1,1,1,1,3,0,3,2,1,0,1,2,0,3,1,3,
|
||||
},
|
||||
{3,4,5,7,-1,-1},
|
||||
{0,4}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,2,2,0,3,3,0,1,2,2,3,2,3,0,2,2,1,1,0,3,3,2,0,2,0,1,0,1,2,3,1,1,
|
||||
0,1,3,3,1,3,3,1,2,3,2,0,0,0,2,2,0,3,1,3,0,3,2,2,0,3,0,3,1,1,0,2,
|
||||
},
|
||||
{0,1,2,5,6,7},
|
||||
{1,6}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,3,0,3,1,1,1,1,2,3,1,3,0,2,3,3,2,0,2,1,1,2,1,1,3,1,0,0,2,0,1,
|
||||
1,3,1,0,0,3,2,3,2,0,3,3,0,0,0,0,1,2,3,3,2,0,3,2,1,0,0,0,2,2,3,3,
|
||||
},
|
||||
{0,2,5,6,7,-1},
|
||||
{2,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
3,2,1,2,1,2,3,2,0,3,2,2,3,1,3,3,0,2,3,0,3,3,2,1,1,1,2,0,2,2,0,1,
|
||||
1,3,3,0,0,3,0,3,0,2,1,3,2,1,0,0,0,1,1,2,0,1,0,0,0,1,3,3,2,0,3,3,
|
||||
},
|
||||
{1,2,3,4,6,7},
|
||||
{5,7}
|
||||
},
|
||||
},
|
||||
{ // 2nd round
|
||||
{
|
||||
{
|
||||
3,3,1,2,0,0,2,2,2,1,2,1,3,1,1,3,3,0,0,3,0,3,3,2,1,1,3,2,3,2,1,3,
|
||||
2,3,0,1,3,2,0,1,2,1,3,1,2,2,3,3,3,1,2,2,0,3,1,2,2,1,3,0,3,0,1,3,
|
||||
},
|
||||
{0,1,3,4,5,7},
|
||||
{0,4}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,0,1,0,0,3,2,0,3,3,1,2,1,3,0,2,0,2,0,0,0,2,3,1,3,1,1,2,3,0,3,0,
|
||||
3,0,2,0,0,2,2,1,0,2,3,3,1,3,1,0,1,3,3,0,0,1,3,1,0,2,0,3,2,1,0,1,
|
||||
},
|
||||
{0,1,3,4,6,-1},
|
||||
{1,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,2,2,3,1,1,0,1,0,1,2,2,3,3,0,2,0,3,2,3,3,0,2,1,0,3,1,0,0,2,3,2,
|
||||
3,2,0,3,2,0,1,0,3,3,1,1,2,2,2,0,2,1,3,1,1,1,1,2,2,2,3,0,1,3,0,0,
|
||||
},
|
||||
{1,2,5,6,7,-1},
|
||||
{2,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,3,3,3,1,3,3,1,0,2,0,2,0,0,3,1,2,1,3,1,2,3,2,2,0,1,3,0,3,3,3,
|
||||
0,0,0,2,1,1,2,3,2,2,3,1,1,2,0,2,0,2,1,3,1,1,3,3,1,1,3,0,2,3,0,0,
|
||||
},
|
||||
{2,3,4,5,6,7},
|
||||
{3,6}
|
||||
},
|
||||
},
|
||||
{ // 3rd round
|
||||
{
|
||||
{
|
||||
0,0,1,0,1,0,0,3,2,0,0,3,0,1,0,2,0,3,0,0,2,0,3,2,2,1,3,2,2,1,1,2,
|
||||
0,0,0,3,0,1,1,0,0,2,1,0,3,1,2,2,2,0,3,1,3,0,1,2,2,1,1,1,0,2,3,1,
|
||||
},
|
||||
{1,2,3,4,5,7},
|
||||
{0,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
1,2,1,0,3,1,1,2,0,0,2,3,2,3,1,3,2,0,3,2,2,3,1,1,1,1,0,3,2,0,0,1,
|
||||
1,0,0,1,3,1,2,3,0,0,2,3,3,0,1,0,0,2,3,0,1,2,0,1,3,3,3,1,2,0,2,1,
|
||||
},
|
||||
{0,2,4,5,6,7},
|
||||
{1,6}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,3,0,2,1,2,0,0,1,1,0,0,3,1,1,0,0,3,0,0,2,3,3,2,3,1,2,0,0,2,3,0,
|
||||
// unused?
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
},
|
||||
{0,2,4,6,7,-1},
|
||||
{2,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,0,1,0,0,1,0,2,3,3,0,3,3,2,3,0,2,2,2,0,3,2,0,3,1,0,0,3,3,0,0,0,
|
||||
2,2,1,0,2,0,3,2,0,0,3,1,3,3,0,0,2,1,1,2,1,0,1,1,0,3,1,2,0,2,0,3,
|
||||
},
|
||||
{0,1,2,3,6,-1},
|
||||
{4,7}
|
||||
},
|
||||
},
|
||||
{ // 4th round
|
||||
{
|
||||
{
|
||||
0,3,3,3,3,3,2,0,0,1,2,0,2,2,2,2,1,1,0,2,2,1,3,2,3,2,0,1,2,3,2,1,
|
||||
3,2,2,3,1,0,1,0,0,2,0,1,2,1,2,3,1,2,1,1,2,2,1,0,1,3,2,3,2,0,3,1,
|
||||
},
|
||||
{0,1,3,4,5,6},
|
||||
{0,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,3,0,0,2,0,3,1,1,1,2,2,2,1,3,1,2,2,1,3,2,2,3,3,0,3,1,0,3,2,0,1,
|
||||
3,0,2,0,1,0,2,1,3,3,1,2,2,0,2,3,3,2,3,0,1,1,3,3,0,2,1,3,0,2,2,3,
|
||||
},
|
||||
{0,1,2,3,5,7},
|
||||
{1,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,2,3,3,3,3,1,2,0,2,3,2,1,0,1,2,2,1,2,0,3,2,0,1,1,0,1,3,1,3,1,
|
||||
3,1,0,0,1,0,0,0,0,1,2,2,1,1,3,3,1,2,3,3,3,2,3,0,2,2,1,3,3,0,2,0,
|
||||
},
|
||||
{2,3,4,5,6,7},
|
||||
{2,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,1,1,3,2,0,3,1,0,1,0,3,2,1,1,2,2,0,3,1,0,1,2,2,2,3,3,0,0,0,0,
|
||||
1,2,1,0,2,1,2,2,2,3,2,3,0,1,3,0,0,1,3,0,0,1,1,0,1,0,0,0,0,2,0,1,
|
||||
},
|
||||
{0,1,2,4,6,7},
|
||||
{4,6}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
const sega_315_5881_crypt_device::sbox sega_315_5881_crypt_device::fn2_sboxes[4][4] = {
|
||||
{ // 1st round
|
||||
{
|
||||
{
|
||||
3,3,0,1,0,1,0,0,0,3,0,0,1,3,1,2,0,3,3,3,2,1,0,1,1,1,2,2,2,3,2,2,
|
||||
2,1,3,3,1,3,1,1,0,0,1,2,0,2,2,1,1,2,3,1,2,1,3,1,2,2,0,1,3,0,2,2,
|
||||
},
|
||||
{1,3,4,5,6,7},
|
||||
{0,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,3,2,1,1,0,0,2,1,0,3,3,0,0,0,3,2,0,2,1,1,2,1,0,0,3,1,2,2,3,1,
|
||||
3,1,3,0,0,0,1,3,1,0,0,3,2,2,3,1,1,3,0,0,2,1,3,3,1,3,1,2,3,1,2,1,
|
||||
},
|
||||
{0,3,5,6,-1,-1},
|
||||
{1,2}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,2,1,0,1,2,1,2,0,1,2,3,3,0,1,3,1,1,2,1,2,1,3,3,2,3,3,2,1,0,1,
|
||||
0,1,0,2,0,1,1,3,2,0,3,2,1,1,1,3,2,3,0,2,3,0,2,2,1,3,0,1,1,2,2,2,
|
||||
},
|
||||
{0,2,3,4,7,-1},
|
||||
{3,4}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,3,1,3,2,0,1,2,0,0,3,3,3,3,3,1,2,0,2,1,2,3,0,2,0,1,0,3,0,2,1,0,
|
||||
2,3,0,1,3,0,3,2,3,1,2,0,3,1,1,2,0,3,0,0,2,0,2,1,2,2,3,2,1,2,3,1,
|
||||
},
|
||||
{1,2,5,6,-1,-1},
|
||||
{5,6}
|
||||
},
|
||||
},
|
||||
{ // 2nd round
|
||||
{
|
||||
{
|
||||
2,3,1,3,1,0,3,3,3,2,3,3,2,0,0,3,2,3,0,3,1,1,2,3,1,1,2,2,0,1,0,0,
|
||||
2,1,0,1,2,0,1,2,0,3,1,1,2,3,1,2,0,2,0,1,3,0,1,0,2,2,3,0,3,2,3,0,
|
||||
},
|
||||
{0,1,4,5,6,7},
|
||||
{0,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,2,0,2,2,0,3,2,3,2,1,3,2,3,3,1,1,0,0,3,0,2,1,1,3,3,2,3,2,0,1,
|
||||
1,2,3,0,1,0,3,0,3,1,0,2,1,2,0,3,2,3,1,2,2,0,3,2,3,0,0,1,2,3,3,3,
|
||||
},
|
||||
{0,2,3,6,7,-1},
|
||||
{1,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
1,2,3,2,0,3,2,3,0,1,1,0,0,2,2,3,2,0,0,3,0,2,3,3,2,2,1,0,2,1,0,3,
|
||||
1,0,2,0,1,1,0,1,0,0,1,0,3,0,3,3,2,2,0,2,1,1,1,0,3,0,1,3,2,3,2,1,
|
||||
},
|
||||
{2,3,4,6,7,-1},
|
||||
{2,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,3,1,3,1,1,2,3,3,1,1,0,1,0,2,3,2,1,0,0,2,2,0,1,0,2,2,2,0,2,1,0,
|
||||
3,1,2,3,1,3,0,2,1,0,1,0,0,1,2,2,3,2,3,1,3,2,1,1,2,0,2,1,3,3,1,0,
|
||||
},
|
||||
{1,2,3,4,5,6},
|
||||
{4,6}
|
||||
},
|
||||
},
|
||||
{ // 3rd round
|
||||
{
|
||||
{
|
||||
0,3,0,1,0,2,3,3,1,0,1,3,2,2,1,1,3,3,3,0,2,0,2,0,0,0,2,3,1,1,0,0,
|
||||
3,3,0,3,3,0,0,2,1,1,1,0,2,2,2,0,3,0,3,1,2,2,0,3,0,0,3,2,0,3,2,1,
|
||||
},
|
||||
{1,4,5,6,7,-1},
|
||||
{0,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,3,0,1,3,0,3,1,3,2,2,2,3,0,3,2,2,1,2,2,0,3,2,2,0,0,2,1,1,3,2,3,
|
||||
2,3,3,1,2,0,1,2,2,1,0,0,0,0,2,3,1,2,0,3,1,3,1,2,3,2,1,0,3,0,0,2,
|
||||
},
|
||||
{0,2,3,4,6,7},
|
||||
{1,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,2,3,2,0,3,2,3,1,1,2,0,2,3,1,3,0,0,0,3,2,0,1,0,1,3,2,3,3,3,1,0,
|
||||
// unused?
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
},
|
||||
{1,2,4,7,-1,-1},
|
||||
{2,4}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,3,1,3,1,1,0,0,1,3,0,2,1,3,3,2,0,2,1,1,2,3,3,0,0,0,2,0,2,3,0,
|
||||
3,3,3,3,2,3,3,2,3,0,1,0,2,3,3,2,0,1,3,1,0,1,2,3,3,0,2,0,3,0,3,3,
|
||||
},
|
||||
{0,1,2,3,5,7},
|
||||
{3,6}
|
||||
},
|
||||
},
|
||||
{ // 4th round
|
||||
{
|
||||
{
|
||||
0,1,1,0,0,1,0,2,3,3,0,1,2,3,0,2,1,0,3,3,2,0,3,0,0,2,1,0,1,0,1,3,
|
||||
0,3,3,1,2,0,3,0,1,3,2,0,3,3,1,3,0,2,3,3,2,1,1,2,2,1,2,1,2,0,1,1,
|
||||
},
|
||||
{0,1,2,4,7,-1},
|
||||
{0,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,0,0,2,3,0,2,3,3,1,1,1,2,1,1,0,0,2,1,0,0,3,1,0,0,3,3,0,1,0,1,2,
|
||||
0,2,0,2,0,1,2,3,2,1,1,0,3,3,3,3,3,3,1,0,3,0,0,2,0,3,2,0,2,2,0,1,
|
||||
},
|
||||
{0,1,3,5,6,-1},
|
||||
{1,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,1,2,1,3,1,1,0,0,3,1,1,1,2,0,3,2,0,1,1,2,3,3,3,0,3,0,0,2,0,3,
|
||||
3,2,0,0,3,2,3,1,2,3,0,3,2,0,1,2,2,2,0,2,0,1,2,2,3,1,2,2,1,1,1,1,
|
||||
},
|
||||
{0,2,3,4,5,7},
|
||||
{2,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,2,0,3,3,0,3,2,1,3,3,0,3,1,1,3,2,3,2,3,0,0,0,3,0,2,2,3,2,2,3,
|
||||
2,2,3,1,2,3,1,2,0,3,0,2,3,1,0,0,3,2,1,2,1,2,1,3,1,0,2,3,3,1,3,2,
|
||||
},
|
||||
{2,3,4,5,6,7},
|
||||
{4,6}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const int sega_315_5881_crypt_device::fn1_game_key_scheduling[38][2] = {
|
||||
{1,29}, {1,71}, {2,4}, {2,54}, {3,8}, {4,56}, {4,73}, {5,11},
|
||||
{6,51}, {7,92}, {8,89}, {9,9}, {9,10}, {9,39}, {9,41}, {9,58},
|
||||
{9,59}, {9,86}, {10,90}, {11,6}, {12,64}, {13,49}, {14,44}, {15,40},
|
||||
{16,69}, {17,15}, {18,23}, {18,43}, {19,82}, {20,81}, {21,32}, {21,61},
|
||||
{22,5}, {23,66}, {24,13}, {24,45}, {25,12}, {25,35}
|
||||
};
|
||||
|
||||
const int sega_315_5881_crypt_device::fn2_game_key_scheduling[34][2] = {
|
||||
{0,0}, {1,3}, {2,11}, {3,20}, {4,22}, {5,23}, {6,29}, {7,38},
|
||||
{8,39}, {9,47}, {9,55}, {9,86}, {9,87}, {9,90}, {10,50}, {10,53},
|
||||
{11,57}, {12,59}, {13,61}, {13,64}, {14,63}, {15,67}, {16,72}, {17,83},
|
||||
{18,88}, {19,94}, {20,35}, {21,17}, {21,92}, {22,6}, {22,11}, {23,85},
|
||||
{24,16}, {25,25}
|
||||
};
|
||||
|
||||
const int sega_315_5881_crypt_device::fn1_sequence_key_scheduling[20][2] = {
|
||||
{0,52}, {1,34}, {2,17}, {3,36}, {4,84}, {4,88}, {5,57}, {6,48},
|
||||
{6,68}, {7,76}, {8,83}, {9,30}, {10,22}, {10,41}, {11,38}, {12,55},
|
||||
{13,74}, {14,19}, {14,80}, {15,26}
|
||||
};
|
||||
|
||||
const int sega_315_5881_crypt_device::fn2_sequence_key_scheduling[16] = {77,34,8,42,36,27,69,66,13,9,79,31,49,7,24,64};
|
||||
|
||||
const int sega_315_5881_crypt_device::fn2_middle_result_scheduling[16] = {1,10,44,68,74,78,81,95,2,4,30,40,41,51,53,58};
|
||||
|
||||
int sega_315_5881_crypt_device::feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys)
|
||||
{
|
||||
int k,m;
|
||||
int aux;
|
||||
int result=0;
|
||||
|
||||
for (m=0; m<4; ++m) { // 4 sboxes
|
||||
for (k=0, aux=0; k<6; ++k)
|
||||
if (sboxes[m].inputs[k]!=-1)
|
||||
aux |= BIT(input, sboxes[m].inputs[k]) << k;
|
||||
|
||||
aux = sboxes[m].table[(aux^subkeys)&0x3f];
|
||||
|
||||
for (k=0; k<2; ++k)
|
||||
result |= BIT(aux,k) << sboxes[m].outputs[k];
|
||||
|
||||
subkeys >>=6;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**************************
|
||||
This implementation is an "educational" version. It must be noted that it can be speed-optimized in a number of ways.
|
||||
The most evident one is to factor out the parts of the key-scheduling that must only be done once (like the game-key &
|
||||
sequence-key parts) as noted in the comments inlined in the function. More sophisticated speed-ups can be gained by
|
||||
noticing that the weak key-scheduling would allow to create some pregenerated look-up tables for doing most of the work
|
||||
of the function. Even so, it would still be pretty slow, so caching techniques could be a wiser option here.
|
||||
**************************/
|
||||
|
||||
UINT16 sega_315_5881_crypt_device::block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data)
|
||||
{
|
||||
int j;
|
||||
int aux,aux2;
|
||||
int A,B;
|
||||
int middle_result;
|
||||
UINT32 fn1_subkeys[4];
|
||||
UINT32 fn2_subkeys[4];
|
||||
|
||||
/* Game-key scheduling; this could be done just once per game at initialization time */
|
||||
memset(fn1_subkeys,0,sizeof(UINT32)*4);
|
||||
memset(fn2_subkeys,0,sizeof(UINT32)*4);
|
||||
|
||||
for (j=0; j<38; ++j) {
|
||||
if (BIT(game_key, fn1_game_key_scheduling[j][0])!=0) {
|
||||
aux = fn1_game_key_scheduling[j][1]%24;
|
||||
aux2 = fn1_game_key_scheduling[j][1]/24;
|
||||
fn1_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
|
||||
for (j=0; j<34; ++j) {
|
||||
if (BIT(game_key, fn2_game_key_scheduling[j][0])!=0) {
|
||||
aux = fn2_game_key_scheduling[j][1]%24;
|
||||
aux2 = fn2_game_key_scheduling[j][1]/24;
|
||||
fn2_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
/********************************************************/
|
||||
|
||||
/* Sequence-key scheduling; this could be done just once per decryption run */
|
||||
for (j=0; j<20; ++j) {
|
||||
if (BIT(sequence_key,fn1_sequence_key_scheduling[j][0])!=0) {
|
||||
aux = fn1_sequence_key_scheduling[j][1]%24;
|
||||
aux2 = fn1_sequence_key_scheduling[j][1]/24;
|
||||
fn1_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
|
||||
for (j=0; j<16; ++j) {
|
||||
if (BIT(sequence_key,j)!=0) {
|
||||
aux = fn2_sequence_key_scheduling[j]%24;
|
||||
aux2 = fn2_sequence_key_scheduling[j]/24;
|
||||
fn2_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
|
||||
// subkeys bits 10 & 41
|
||||
fn2_subkeys[0] ^= (BIT(sequence_key,2)<<10);
|
||||
fn2_subkeys[1] ^= (BIT(sequence_key,4)<<17);
|
||||
/**************************************************************/
|
||||
|
||||
// First Feistel Network
|
||||
|
||||
aux = BITSWAP16(counter,5,12,14,13,9,3,6,4, 8,1,15,11,0,7,10,2);
|
||||
|
||||
// 1st round
|
||||
B = aux >> 8;
|
||||
A = (aux & 0xff) ^ feistel_function(B,fn1_sboxes[0],fn1_subkeys[0]);
|
||||
|
||||
// 2nd round
|
||||
B = B ^ feistel_function(A,fn1_sboxes[1],fn1_subkeys[1]);
|
||||
|
||||
// 3rd round
|
||||
A = A ^ feistel_function(B,fn1_sboxes[2],fn1_subkeys[2]);
|
||||
|
||||
// 4th round
|
||||
B = B ^ feistel_function(A,fn1_sboxes[3],fn1_subkeys[3]);
|
||||
|
||||
middle_result = (B<<8)|A;
|
||||
|
||||
|
||||
/* Middle-result-key sheduling */
|
||||
for (j=0; j<16; ++j) {
|
||||
if (BIT(middle_result,j)!=0) {
|
||||
aux = fn2_middle_result_scheduling[j]%24;
|
||||
aux2 = fn2_middle_result_scheduling[j]/24;
|
||||
fn2_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
/*********************/
|
||||
|
||||
// Second Feistel Network
|
||||
|
||||
aux = BITSWAP16(data,14,3,8,12,13,7,15,4, 6,2,9,5,11,0,1,10);
|
||||
|
||||
// 1st round
|
||||
B = aux >> 8;
|
||||
A = (aux & 0xff) ^ feistel_function(B,fn2_sboxes[0],fn2_subkeys[0]);
|
||||
|
||||
// 2nd round
|
||||
B = B ^ feistel_function(A,fn2_sboxes[1],fn2_subkeys[1]);
|
||||
|
||||
// 3rd round
|
||||
A = A ^ feistel_function(B,fn2_sboxes[2],fn2_subkeys[2]);
|
||||
|
||||
// 4th round
|
||||
B = B ^ feistel_function(A,fn2_sboxes[3],fn2_subkeys[3]);
|
||||
|
||||
aux = (B<<8)|A;
|
||||
|
||||
aux = BITSWAP16(aux,15,7,6,14,13,12,5,4, 3,2,11,10,9,1,0,8);
|
||||
|
||||
return aux;
|
||||
}
|
||||
|
||||
UINT16 sega_315_5881_crypt_device::get_decrypted_16()
|
||||
{
|
||||
UINT16 enc;
|
||||
|
||||
enc = m_read(prot_cur_address);
|
||||
|
||||
UINT16 dec = block_decrypt(key, subkey, prot_cur_address, enc);
|
||||
UINT16 res = (dec & 3) | (dec_hist & 0xfffc);
|
||||
dec_hist = dec;
|
||||
|
||||
prot_cur_address ++;
|
||||
return res;
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::enc_start()
|
||||
{
|
||||
buffer_pos = BUFFER_SIZE;
|
||||
dec_header = get_decrypted_16() << 16;
|
||||
dec_header |= get_decrypted_16();
|
||||
|
||||
if(dec_header & FLAG_COMPRESSED) {
|
||||
line_buffer_size = dec_header & FLAG_LINE_SIZE_512 ? 512 : 256;
|
||||
line_buffer_pos = line_buffer_size;
|
||||
buffer_bit = 7;
|
||||
}
|
||||
enc_ready = true;
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::enc_fill()
|
||||
{
|
||||
assert(buffer_pos == BUFFER_SIZE);
|
||||
for(int i = 0; i != BUFFER_SIZE; i+=2) {
|
||||
UINT16 val = get_decrypted_16();
|
||||
buffer[i] = val;
|
||||
buffer[i+1] = val >> 8;
|
||||
}
|
||||
buffer_pos = 0;
|
||||
}
|
||||
|
||||
/* node format
|
||||
0xxxxxxx - next node index
|
||||
1a0bbccc - end node
|
||||
a - 0 = repeat
|
||||
1 = fetch
|
||||
b - if a = 1
|
||||
00 - fetch 0
|
||||
01 - fetch 1
|
||||
11 - fetch -1
|
||||
if a = 0
|
||||
000
|
||||
c - repeat/fetch counter
|
||||
count = ccc + 1
|
||||
11111111 - empty node
|
||||
*/
|
||||
const UINT8 sega_315_5881_crypt_device::trees[9][2][32] = {
|
||||
{
|
||||
{0x01,0x10,0x0f,0x05,0xc4,0x13,0x87,0x0a,0xcc,0x81,0xce,0x0c,0x86,0x0e,0x84,0xc2,
|
||||
0x11,0xc1,0xc3,0xcf,0x15,0xc8,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc7,0x02,0x03,0x04,0x80,0x06,0x07,0x08,0x09,0xc9,0x0b,0x0d,0x82,0x83,0x85,0xc0,
|
||||
0x12,0xc6,0xc5,0x14,0x16,0xca,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x02,0x80,0x05,0x04,0x81,0x10,0x15,0x82,0x09,0x83,0x0b,0x0c,0x0d,0xdc,0x0f,0xde,
|
||||
0x1c,0xcf,0xc5,0xdd,0x86,0x16,0x87,0x18,0x19,0x1a,0xda,0xca,0xc9,0x1e,0xce,0xff,},
|
||||
{0x01,0x17,0x03,0x0a,0x08,0x06,0x07,0xc2,0xd9,0xc4,0xd8,0xc8,0x0e,0x84,0xcb,0x85,
|
||||
0x11,0x12,0x13,0x14,0xcd,0x1b,0xdb,0xc7,0xc0,0xc1,0x1d,0xdf,0xc3,0xc6,0xcc,0xff,},
|
||||
},
|
||||
{
|
||||
{0xc6,0x80,0x03,0x0b,0x05,0x07,0x82,0x08,0x15,0xdc,0xdd,0x0c,0xd9,0xc2,0x14,0x10,
|
||||
0x85,0x86,0x18,0x16,0xc5,0xc4,0xc8,0xc9,0xc0,0xcc,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0x01,0x02,0x12,0x04,0x81,0x06,0x83,0xc3,0x09,0x0a,0x84,0x11,0x0d,0x0e,0x0f,0x19,
|
||||
0xca,0xc1,0x13,0xd8,0xda,0xdb,0x17,0xde,0xcd,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x80,0x0d,0x04,0x05,0x15,0x83,0x08,0xd9,0x10,0x0b,0x0c,0x84,0x0e,0xc0,0x14,
|
||||
0x12,0xcb,0x13,0xca,0xc8,0xc2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc5,0x02,0x03,0x07,0x81,0x06,0x82,0xcc,0x09,0x0a,0xc9,0x11,0xc4,0x0f,0x85,0xd8,
|
||||
0xda,0xdb,0xc3,0xdc,0xdd,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x80,0x06,0x0c,0x05,0x81,0xd8,0x84,0x09,0xdc,0x0b,0x0f,0x0d,0x0e,0x10,0xdb,
|
||||
0x11,0xca,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc4,0x02,0x03,0x04,0xcb,0x0a,0x07,0x08,0xd9,0x82,0xc8,0x83,0xc0,0xc1,0xda,0xc2,
|
||||
0xc9,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x02,0x06,0x0a,0x83,0x0b,0x07,0x08,0x09,0x82,0xd8,0x0c,0xd9,0xda,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc3,0x80,0x03,0x04,0x05,0x81,0xca,0xc8,0xdb,0xc9,0xc0,0xc1,0x0d,0xc2,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x02,0x03,0x04,0x81,0x07,0x08,0xd8,0xda,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc2,0x80,0x05,0xc9,0xc8,0x06,0x82,0xc0,0x09,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x80,0x04,0xc8,0xc0,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc1,0x02,0x03,0x81,0x05,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc0,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
};
|
||||
|
||||
int sega_315_5881_crypt_device::get_compressed_bit()
|
||||
{
|
||||
if(buffer_pos == BUFFER_SIZE)
|
||||
enc_fill();
|
||||
int res = (buffer[buffer_pos^1] >> buffer_bit) & 1;
|
||||
buffer_bit--;
|
||||
if(buffer_bit == -1) {
|
||||
buffer_bit = 7;
|
||||
buffer_pos++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void sega_315_5881_crypt_device::line_fill()
|
||||
{
|
||||
assert(line_buffer_pos == line_buffer_size);
|
||||
UINT8 *lp = line_buffer;
|
||||
UINT8 *lc = line_buffer_prev;
|
||||
line_buffer = lc;
|
||||
line_buffer_prev = lp;
|
||||
line_buffer_pos = 0;
|
||||
|
||||
UINT32 line_buffer_mask = line_buffer_size-1;
|
||||
|
||||
for(int i=0; i != line_buffer_size;) {
|
||||
// vlc 0: start of line
|
||||
// vlc 1: interior of line
|
||||
// vlc 2-9: 7-1 bytes from end of line
|
||||
|
||||
int slot = i ? i < line_buffer_size - 7 ? 1 : (i & 7) + 1 : 0;
|
||||
|
||||
UINT32 tmp = 0;
|
||||
while (!(tmp&0x80))
|
||||
if(get_compressed_bit())
|
||||
tmp = trees[slot][1][tmp];
|
||||
else
|
||||
tmp = trees[slot][0][tmp];
|
||||
if(tmp != 0xff) {
|
||||
int count = (tmp & 7) + 1;
|
||||
|
||||
if(tmp&0x40) {
|
||||
// Copy from previous line
|
||||
|
||||
static int offsets[4] = {0, 1, 0, -1};
|
||||
int offset = offsets[(tmp & 0x18) >> 3];
|
||||
for(int j=0; j != count; j++) {
|
||||
lc[i^1] = lp[((i+offset) & line_buffer_mask)^1];
|
||||
i++;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Get a byte in the stream and write n times
|
||||
UINT8 byte;
|
||||
byte = get_compressed_bit() << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = byte | get_compressed_bit();
|
||||
for(int j=0; j != count; j++)
|
||||
lc[(i++)^1] = byte;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
src/mame/machine/315-5881_crypt.h
Normal file
87
src/mame/machine/315-5881_crypt.h
Normal file
@ -0,0 +1,87 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __SEGA315_5881_CRYPT__
|
||||
#define __SEGA315_5881_CRYPT__
|
||||
|
||||
typedef device_delegate<UINT16 (UINT32)> sega_m2_read_delegate;
|
||||
|
||||
extern const device_type SEGA315_5881_CRYPT;
|
||||
|
||||
#define MCFG_SET_READ_CALLBACK( _class, _method) \
|
||||
sega_315_5881_crypt_device::set_read_cb(*device, sega_m2_read_delegate(&_class::_method, #_class "::" #_method, NULL, (_class *)0));
|
||||
|
||||
|
||||
class sega_315_5881_crypt_device : public device_t
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
sega_315_5881_crypt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
|
||||
|
||||
void do_decrypt(UINT8 *&base);
|
||||
void set_addr_low(UINT16 data);
|
||||
void set_addr_high(UINT16 data);
|
||||
void set_subkey(UINT16 data);
|
||||
void set_key(UINT32 data);
|
||||
|
||||
sega_m2_read_delegate m_read;
|
||||
|
||||
static void set_read_cb(device_t &device,sega_m2_read_delegate readcb)
|
||||
{
|
||||
sega_315_5881_crypt_device &dev = downcast<sega_315_5881_crypt_device &>(device);
|
||||
dev.m_read = readcb;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void device_start();
|
||||
virtual void device_reset();
|
||||
|
||||
private:
|
||||
|
||||
enum {
|
||||
BUFFER_SIZE = 32768, LINE_SIZE = 512,
|
||||
FLAG_COMPRESSED = 0x10000, FLAG_LINE_SIZE_512 = 0x20000
|
||||
};
|
||||
|
||||
UINT32 key;
|
||||
|
||||
UINT8 *buffer, *line_buffer, *line_buffer_prev;
|
||||
UINT32 prot_cur_address;
|
||||
UINT16 subkey, dec_hist;
|
||||
UINT32 dec_header;
|
||||
|
||||
bool enc_ready;
|
||||
|
||||
int buffer_pos, line_buffer_pos, line_buffer_size, buffer_bit;
|
||||
|
||||
struct sbox {
|
||||
UINT8 table[64];
|
||||
int inputs[6]; // positions of the inputs bits, -1 means no input except from key
|
||||
int outputs[2]; // positions of the output bits
|
||||
};
|
||||
|
||||
static const sbox fn1_sboxes[4][4];
|
||||
static const sbox fn2_sboxes[4][4];
|
||||
|
||||
static const int fn1_game_key_scheduling[38][2];
|
||||
static const int fn2_game_key_scheduling[34][2];
|
||||
static const int fn1_sequence_key_scheduling[20][2];
|
||||
static const int fn2_sequence_key_scheduling[16];
|
||||
static const int fn2_middle_result_scheduling[16];
|
||||
|
||||
static const UINT8 trees[9][2][32];
|
||||
|
||||
int feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys);
|
||||
UINT16 block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data);
|
||||
|
||||
UINT16 get_decrypted_16();
|
||||
int get_compressed_bit();
|
||||
|
||||
void enc_start();
|
||||
void enc_fill();
|
||||
void line_fill();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
@ -105,7 +105,8 @@ note: if ROM is not mounted its area readed as 0xFF
|
||||
const device_type NAOMI_M2_BOARD = &device_creator<naomi_m2_board>;
|
||||
|
||||
naomi_m2_board::naomi_m2_board(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: naomi_board(mconfig, NAOMI_M2_BOARD, "Sega NAOMI M2 BOARD", tag, owner, clock, "naomi_m2_board", __FILE__)
|
||||
: naomi_board(mconfig, NAOMI_M2_BOARD, "Sega NAOMI M2 BOARD", tag, owner, clock, "naomi_m2_board", __FILE__),
|
||||
m_cryptdevice(*this, "segam2crypt")
|
||||
{
|
||||
key_tag = 0;
|
||||
}
|
||||
@ -120,30 +121,10 @@ void naomi_m2_board::device_start()
|
||||
{
|
||||
naomi_board::device_start();
|
||||
|
||||
#if USE_NAOMICRYPT
|
||||
key = get_naomi_key(machine());
|
||||
#else
|
||||
const UINT8 *key_data = memregion(key_tag)->base();
|
||||
key = (key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | key_data[3];
|
||||
#endif
|
||||
ram = auto_alloc_array(machine(), UINT8, RAM_SIZE);
|
||||
buffer = auto_alloc_array(machine(), UINT8, BUFFER_SIZE);
|
||||
line_buffer = auto_alloc_array(machine(), UINT8, LINE_SIZE);
|
||||
line_buffer_prev = auto_alloc_array(machine(), UINT8, LINE_SIZE);
|
||||
|
||||
save_pointer(NAME(ram), RAM_SIZE);
|
||||
save_pointer(NAME(buffer), BUFFER_SIZE);
|
||||
save_pointer(NAME(line_buffer), LINE_SIZE);
|
||||
save_pointer(NAME(line_buffer_prev), LINE_SIZE);
|
||||
save_item(NAME(rom_cur_address));
|
||||
save_item(NAME(prot_cur_address));
|
||||
save_item(NAME(subkey));
|
||||
save_item(NAME(enc_ready));
|
||||
save_item(NAME(dec_hist));
|
||||
save_item(NAME(dec_header));
|
||||
save_item(NAME(buffer_pos));
|
||||
save_item(NAME(line_buffer_pos));
|
||||
save_item(NAME(line_buffer_size));
|
||||
save_pointer(NAME(ram), RAM_SIZE);
|
||||
}
|
||||
|
||||
void naomi_m2_board::device_reset()
|
||||
@ -151,21 +132,15 @@ void naomi_m2_board::device_reset()
|
||||
naomi_board::device_reset();
|
||||
|
||||
memset(ram, 0, RAM_SIZE);
|
||||
memset(buffer, 0, BUFFER_SIZE);
|
||||
memset(line_buffer, 0, LINE_SIZE);
|
||||
memset(line_buffer_prev, 0, LINE_SIZE);
|
||||
|
||||
rom_cur_address = 0;
|
||||
prot_cur_address = 0;
|
||||
subkey = 0;
|
||||
dec_hist = 0;
|
||||
dec_header = 0;
|
||||
enc_ready = false;
|
||||
|
||||
buffer_pos = 0;
|
||||
line_buffer_pos = 0;
|
||||
line_buffer_size = 0;
|
||||
buffer_bit = 0;
|
||||
#if USE_NAOMICRYPT
|
||||
m_cryptdevice->set_key(get_naomi_key(machine()));
|
||||
#else
|
||||
const UINT8 *key_data = memregion(key_tag)->base();
|
||||
m_cryptdevice->set_key((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | key_data[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void naomi_m2_board::board_setup_address(UINT32 address, bool is_dma)
|
||||
@ -175,21 +150,10 @@ void naomi_m2_board::board_setup_address(UINT32 address, bool is_dma)
|
||||
|
||||
void naomi_m2_board::board_get_buffer(UINT8 *&base, UINT32 &limit)
|
||||
{
|
||||
|
||||
if(rom_cur_address & 0x40000000) {
|
||||
if(rom_cur_address == 0x4001fffe) {
|
||||
if(!enc_ready)
|
||||
enc_start();
|
||||
if(dec_header & FLAG_COMPRESSED) {
|
||||
if(line_buffer_pos == line_buffer_size)
|
||||
line_fill();
|
||||
base = line_buffer + line_buffer_pos;
|
||||
line_buffer_pos += 2;
|
||||
} else {
|
||||
if(buffer_pos == BUFFER_SIZE)
|
||||
enc_fill();
|
||||
base = buffer + buffer_pos;
|
||||
buffer_pos += 2;
|
||||
}
|
||||
m_cryptdevice->do_decrypt(base);
|
||||
limit = 2;
|
||||
|
||||
} else
|
||||
@ -214,6 +178,8 @@ void naomi_m2_board::board_advance(UINT32 size)
|
||||
|
||||
void naomi_m2_board::board_write(offs_t offset, UINT16 data)
|
||||
{
|
||||
|
||||
|
||||
if(offset & 0x40000000) {
|
||||
if((offset & 0x0f000000) == 0x02000000) {
|
||||
offset &= RAM_SIZE-1;
|
||||
@ -222,724 +188,35 @@ void naomi_m2_board::board_write(offs_t offset, UINT16 data)
|
||||
return;
|
||||
}
|
||||
switch(offset & 0x1fffffff) {
|
||||
case 0x1fff8: prot_cur_address = (prot_cur_address & 0xffff0000) | data; enc_ready = false; return;
|
||||
case 0x1fffa: prot_cur_address = (prot_cur_address & 0x0000ffff) | (data << 16); enc_ready = false; return;
|
||||
case 0x1fffc: subkey = data; enc_ready = false; return;
|
||||
|
||||
case 0x1fff8: m_cryptdevice->set_addr_low(data); return;
|
||||
case 0x1fffa: m_cryptdevice->set_addr_high(data); return;
|
||||
case 0x1fffc: m_cryptdevice->set_subkey(data); return;
|
||||
}
|
||||
}
|
||||
throw emu_fatalerror("NAOMIM2: unhandled board write %08x, %04x\n", offset, data);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
DECRYPTION EMULATION
|
||||
|
||||
By convention, we label the three known cart protection methods this way (using Deunan Knute's wording):
|
||||
M1: DMA read of protected ROM area
|
||||
M2: special read of ROM area which supplies decryption key first
|
||||
M3: normal read followed by write to cart's decryption buffer (up to 64kB), followed by M2 but from buffer area
|
||||
|
||||
Notes below refer to M2 & M3.
|
||||
|
||||
The encryption is done by a stream cipher operating in counter mode, which use a 16-bits internal block cipher.
|
||||
|
||||
There are 2 "control bits" at the start of the decrypted stream which control the mode of operation: bit #1 set to 1 means
|
||||
that the decrypted stream needs to be decompressed after being decrypted. More on this later.
|
||||
|
||||
The next 16-bits are part of the header (they don't belong to the plaintext), but his meaning is unclear. It has been
|
||||
conjectured that it could stablish when to "reset" the process and start processing a new stream (based on some tests
|
||||
on WWFROYAL, in which the decryption's output doesn't seem to be valid for more than some dozens of words), but some
|
||||
more testing would be needed for clarifying that.
|
||||
|
||||
After those 18 heading bits, we find the proper plaintext. It must be noted that, due to the initial 2 special bits,
|
||||
the 16-bits words of the plaintext are shifted 2 bits respect to the word-boundaries of the output stream of the
|
||||
internal block-cipher. So, at a given step, the internal block cipher will output 16-bits, 14 of which will go to a
|
||||
given plaintext word, and the remaining 2 to the next plaintext word.
|
||||
|
||||
The underlying block cipher consists of two 4-round Feistel Networks (FN): the first one takes the counter (16 bits),
|
||||
the game-key (>=26 bits) and the sequence-key (16 bits) and output a middle result (16 bits) which will act as another key
|
||||
for the second one. The second FN will take the encrypted word (16 bits), the game-key, the sequence-key and the result
|
||||
from the first FN and will output the decrypted word (16 bits).
|
||||
|
||||
Each round of the Feistel Networks use four substitution sboxes, each having 6 inputs and 2 outputs. The input can be the
|
||||
XOR of at most two "sources bits", being source bits the bits from the previous round and the bits from the different keys.
|
||||
|
||||
The underlying block cipher has the same structure than the one used by the CPS-2 (Capcom Play System 2) and,
|
||||
indeed, some of the used sboxes are exactly the same and appear in the same FN/round in both systems (this is not evident,
|
||||
as you need to apply a bitswapping and some XORs to the input & output of the sboxes to get the same values due). However,
|
||||
the key scheduling used by this implementation is much weaker than the CPS-2's one. Many s-boxes inputs aren't XORed with any
|
||||
key bit.
|
||||
|
||||
Due to the small key-length, no sophisticated attacks are needed to recover the keys; a brute-force attack knowing just
|
||||
some (encrypted word-decrypted word) pairs suffice. However, due to the weak key scheduling, it should be noted that some
|
||||
related keys can produce the same output bytes for some (short) input sequences.
|
||||
|
||||
The only difference in the decryption process between M2 and M3 is the initialization of the counter. In M3, the counter is
|
||||
always set to 0 at the beginning of the decryption while, in M2, the bits #1-#16 of the ciphertext's address are used
|
||||
to initialize the counter.
|
||||
|
||||
Note that this implementation considers that the counter initialization for ram decryption is 0 simply because the ram is
|
||||
mapped to multiples of 128K.
|
||||
|
||||
Due to the nature of the cipher, there are some degrees of freedom when choosing the s-boxes and keys values; by example,
|
||||
you could apply a fixed bitswapping and XOR to the keys and the decryption would remain the same as long as you change
|
||||
accordingly the s-boxes' definitions. So the order of the bits in the keys is arbitrary, and the s-boxes values have been
|
||||
chosen so as to make the key for CAPSNK equal to 0.
|
||||
|
||||
It can be observed that a couple of sboxes have incomplete tables (a 255 value indicate an unknown value). The recovered keys
|
||||
as of december/2010 show small randomness and big correlations, making possible that some unseen bits could make the
|
||||
decryption need those incomplete parts.
|
||||
|
||||
****************************************************************************************/
|
||||
|
||||
const naomi_m2_board::sbox naomi_m2_board::fn1_sboxes[4][4] = {
|
||||
{ // 1st round
|
||||
{
|
||||
{
|
||||
0,3,2,2,1,3,1,2,3,2,1,2,1,2,3,1,3,2,2,0,2,1,3,0,0,3,2,3,2,1,2,0,
|
||||
2,3,1,1,2,2,1,1,1,0,2,3,3,0,2,1,1,1,1,1,3,0,3,2,1,0,1,2,0,3,1,3,
|
||||
},
|
||||
{3,4,5,7,-1,-1},
|
||||
{0,4}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,2,2,0,3,3,0,1,2,2,3,2,3,0,2,2,1,1,0,3,3,2,0,2,0,1,0,1,2,3,1,1,
|
||||
0,1,3,3,1,3,3,1,2,3,2,0,0,0,2,2,0,3,1,3,0,3,2,2,0,3,0,3,1,1,0,2,
|
||||
},
|
||||
{0,1,2,5,6,7},
|
||||
{1,6}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,3,0,3,1,1,1,1,2,3,1,3,0,2,3,3,2,0,2,1,1,2,1,1,3,1,0,0,2,0,1,
|
||||
1,3,1,0,0,3,2,3,2,0,3,3,0,0,0,0,1,2,3,3,2,0,3,2,1,0,0,0,2,2,3,3,
|
||||
},
|
||||
{0,2,5,6,7,-1},
|
||||
{2,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
3,2,1,2,1,2,3,2,0,3,2,2,3,1,3,3,0,2,3,0,3,3,2,1,1,1,2,0,2,2,0,1,
|
||||
1,3,3,0,0,3,0,3,0,2,1,3,2,1,0,0,0,1,1,2,0,1,0,0,0,1,3,3,2,0,3,3,
|
||||
},
|
||||
{1,2,3,4,6,7},
|
||||
{5,7}
|
||||
},
|
||||
},
|
||||
{ // 2nd round
|
||||
{
|
||||
{
|
||||
3,3,1,2,0,0,2,2,2,1,2,1,3,1,1,3,3,0,0,3,0,3,3,2,1,1,3,2,3,2,1,3,
|
||||
2,3,0,1,3,2,0,1,2,1,3,1,2,2,3,3,3,1,2,2,0,3,1,2,2,1,3,0,3,0,1,3,
|
||||
},
|
||||
{0,1,3,4,5,7},
|
||||
{0,4}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,0,1,0,0,3,2,0,3,3,1,2,1,3,0,2,0,2,0,0,0,2,3,1,3,1,1,2,3,0,3,0,
|
||||
3,0,2,0,0,2,2,1,0,2,3,3,1,3,1,0,1,3,3,0,0,1,3,1,0,2,0,3,2,1,0,1,
|
||||
},
|
||||
{0,1,3,4,6,-1},
|
||||
{1,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,2,2,3,1,1,0,1,0,1,2,2,3,3,0,2,0,3,2,3,3,0,2,1,0,3,1,0,0,2,3,2,
|
||||
3,2,0,3,2,0,1,0,3,3,1,1,2,2,2,0,2,1,3,1,1,1,1,2,2,2,3,0,1,3,0,0,
|
||||
},
|
||||
{1,2,5,6,7,-1},
|
||||
{2,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,3,3,3,1,3,3,1,0,2,0,2,0,0,3,1,2,1,3,1,2,3,2,2,0,1,3,0,3,3,3,
|
||||
0,0,0,2,1,1,2,3,2,2,3,1,1,2,0,2,0,2,1,3,1,1,3,3,1,1,3,0,2,3,0,0,
|
||||
},
|
||||
{2,3,4,5,6,7},
|
||||
{3,6}
|
||||
},
|
||||
},
|
||||
{ // 3rd round
|
||||
{
|
||||
{
|
||||
0,0,1,0,1,0,0,3,2,0,0,3,0,1,0,2,0,3,0,0,2,0,3,2,2,1,3,2,2,1,1,2,
|
||||
0,0,0,3,0,1,1,0,0,2,1,0,3,1,2,2,2,0,3,1,3,0,1,2,2,1,1,1,0,2,3,1,
|
||||
},
|
||||
{1,2,3,4,5,7},
|
||||
{0,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
1,2,1,0,3,1,1,2,0,0,2,3,2,3,1,3,2,0,3,2,2,3,1,1,1,1,0,3,2,0,0,1,
|
||||
1,0,0,1,3,1,2,3,0,0,2,3,3,0,1,0,0,2,3,0,1,2,0,1,3,3,3,1,2,0,2,1,
|
||||
},
|
||||
{0,2,4,5,6,7},
|
||||
{1,6}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,3,0,2,1,2,0,0,1,1,0,0,3,1,1,0,0,3,0,0,2,3,3,2,3,1,2,0,0,2,3,0,
|
||||
// unused?
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
},
|
||||
{0,2,4,6,7,-1},
|
||||
{2,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,0,1,0,0,1,0,2,3,3,0,3,3,2,3,0,2,2,2,0,3,2,0,3,1,0,0,3,3,0,0,0,
|
||||
2,2,1,0,2,0,3,2,0,0,3,1,3,3,0,0,2,1,1,2,1,0,1,1,0,3,1,2,0,2,0,3,
|
||||
},
|
||||
{0,1,2,3,6,-1},
|
||||
{4,7}
|
||||
},
|
||||
},
|
||||
{ // 4th round
|
||||
{
|
||||
{
|
||||
0,3,3,3,3,3,2,0,0,1,2,0,2,2,2,2,1,1,0,2,2,1,3,2,3,2,0,1,2,3,2,1,
|
||||
3,2,2,3,1,0,1,0,0,2,0,1,2,1,2,3,1,2,1,1,2,2,1,0,1,3,2,3,2,0,3,1,
|
||||
},
|
||||
{0,1,3,4,5,6},
|
||||
{0,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,3,0,0,2,0,3,1,1,1,2,2,2,1,3,1,2,2,1,3,2,2,3,3,0,3,1,0,3,2,0,1,
|
||||
3,0,2,0,1,0,2,1,3,3,1,2,2,0,2,3,3,2,3,0,1,1,3,3,0,2,1,3,0,2,2,3,
|
||||
},
|
||||
{0,1,2,3,5,7},
|
||||
{1,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,2,3,3,3,3,1,2,0,2,3,2,1,0,1,2,2,1,2,0,3,2,0,1,1,0,1,3,1,3,1,
|
||||
3,1,0,0,1,0,0,0,0,1,2,2,1,1,3,3,1,2,3,3,3,2,3,0,2,2,1,3,3,0,2,0,
|
||||
},
|
||||
{2,3,4,5,6,7},
|
||||
{2,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,1,1,3,2,0,3,1,0,1,0,3,2,1,1,2,2,0,3,1,0,1,2,2,2,3,3,0,0,0,0,
|
||||
1,2,1,0,2,1,2,2,2,3,2,3,0,1,3,0,0,1,3,0,0,1,1,0,1,0,0,0,0,2,0,1,
|
||||
},
|
||||
{0,1,2,4,6,7},
|
||||
{4,6}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
const naomi_m2_board::sbox naomi_m2_board::fn2_sboxes[4][4] = {
|
||||
{ // 1st round
|
||||
{
|
||||
{
|
||||
3,3,0,1,0,1,0,0,0,3,0,0,1,3,1,2,0,3,3,3,2,1,0,1,1,1,2,2,2,3,2,2,
|
||||
2,1,3,3,1,3,1,1,0,0,1,2,0,2,2,1,1,2,3,1,2,1,3,1,2,2,0,1,3,0,2,2,
|
||||
},
|
||||
{1,3,4,5,6,7},
|
||||
{0,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,3,2,1,1,0,0,2,1,0,3,3,0,0,0,3,2,0,2,1,1,2,1,0,0,3,1,2,2,3,1,
|
||||
3,1,3,0,0,0,1,3,1,0,0,3,2,2,3,1,1,3,0,0,2,1,3,3,1,3,1,2,3,1,2,1,
|
||||
},
|
||||
{0,3,5,6,-1,-1},
|
||||
{1,2}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,2,1,0,1,2,1,2,0,1,2,3,3,0,1,3,1,1,2,1,2,1,3,3,2,3,3,2,1,0,1,
|
||||
0,1,0,2,0,1,1,3,2,0,3,2,1,1,1,3,2,3,0,2,3,0,2,2,1,3,0,1,1,2,2,2,
|
||||
},
|
||||
{0,2,3,4,7,-1},
|
||||
{3,4}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,3,1,3,2,0,1,2,0,0,3,3,3,3,3,1,2,0,2,1,2,3,0,2,0,1,0,3,0,2,1,0,
|
||||
2,3,0,1,3,0,3,2,3,1,2,0,3,1,1,2,0,3,0,0,2,0,2,1,2,2,3,2,1,2,3,1,
|
||||
},
|
||||
{1,2,5,6,-1,-1},
|
||||
{5,6}
|
||||
},
|
||||
},
|
||||
{ // 2nd round
|
||||
{
|
||||
{
|
||||
2,3,1,3,1,0,3,3,3,2,3,3,2,0,0,3,2,3,0,3,1,1,2,3,1,1,2,2,0,1,0,0,
|
||||
2,1,0,1,2,0,1,2,0,3,1,1,2,3,1,2,0,2,0,1,3,0,1,0,2,2,3,0,3,2,3,0,
|
||||
},
|
||||
{0,1,4,5,6,7},
|
||||
{0,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,2,0,2,2,0,3,2,3,2,1,3,2,3,3,1,1,0,0,3,0,2,1,1,3,3,2,3,2,0,1,
|
||||
1,2,3,0,1,0,3,0,3,1,0,2,1,2,0,3,2,3,1,2,2,0,3,2,3,0,0,1,2,3,3,3,
|
||||
},
|
||||
{0,2,3,6,7,-1},
|
||||
{1,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
1,2,3,2,0,3,2,3,0,1,1,0,0,2,2,3,2,0,0,3,0,2,3,3,2,2,1,0,2,1,0,3,
|
||||
1,0,2,0,1,1,0,1,0,0,1,0,3,0,3,3,2,2,0,2,1,1,1,0,3,0,1,3,2,3,2,1,
|
||||
},
|
||||
{2,3,4,6,7,-1},
|
||||
{2,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,3,1,3,1,1,2,3,3,1,1,0,1,0,2,3,2,1,0,0,2,2,0,1,0,2,2,2,0,2,1,0,
|
||||
3,1,2,3,1,3,0,2,1,0,1,0,0,1,2,2,3,2,3,1,3,2,1,1,2,0,2,1,3,3,1,0,
|
||||
},
|
||||
{1,2,3,4,5,6},
|
||||
{4,6}
|
||||
},
|
||||
},
|
||||
{ // 3rd round
|
||||
{
|
||||
{
|
||||
0,3,0,1,0,2,3,3,1,0,1,3,2,2,1,1,3,3,3,0,2,0,2,0,0,0,2,3,1,1,0,0,
|
||||
3,3,0,3,3,0,0,2,1,1,1,0,2,2,2,0,3,0,3,1,2,2,0,3,0,0,3,2,0,3,2,1,
|
||||
},
|
||||
{1,4,5,6,7,-1},
|
||||
{0,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,3,0,1,3,0,3,1,3,2,2,2,3,0,3,2,2,1,2,2,0,3,2,2,0,0,2,1,1,3,2,3,
|
||||
2,3,3,1,2,0,1,2,2,1,0,0,0,0,2,3,1,2,0,3,1,3,1,2,3,2,1,0,3,0,0,2,
|
||||
},
|
||||
{0,2,3,4,6,7},
|
||||
{1,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,2,3,2,0,3,2,3,1,1,2,0,2,3,1,3,0,0,0,3,2,0,1,0,1,3,2,3,3,3,1,0,
|
||||
// unused?
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
},
|
||||
{1,2,4,7,-1,-1},
|
||||
{2,4}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,2,3,1,3,1,1,0,0,1,3,0,2,1,3,3,2,0,2,1,1,2,3,3,0,0,0,2,0,2,3,0,
|
||||
3,3,3,3,2,3,3,2,3,0,1,0,2,3,3,2,0,1,3,1,0,1,2,3,3,0,2,0,3,0,3,3,
|
||||
},
|
||||
{0,1,2,3,5,7},
|
||||
{3,6}
|
||||
},
|
||||
},
|
||||
{ // 4th round
|
||||
{
|
||||
{
|
||||
0,1,1,0,0,1,0,2,3,3,0,1,2,3,0,2,1,0,3,3,2,0,3,0,0,2,1,0,1,0,1,3,
|
||||
0,3,3,1,2,0,3,0,1,3,2,0,3,3,1,3,0,2,3,3,2,1,1,2,2,1,2,1,2,0,1,1,
|
||||
},
|
||||
{0,1,2,4,7,-1},
|
||||
{0,5}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
2,0,0,2,3,0,2,3,3,1,1,1,2,1,1,0,0,2,1,0,0,3,1,0,0,3,3,0,1,0,1,2,
|
||||
0,2,0,2,0,1,2,3,2,1,1,0,3,3,3,3,3,3,1,0,3,0,0,2,0,3,2,0,2,2,0,1,
|
||||
},
|
||||
{0,1,3,5,6,-1},
|
||||
{1,3}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,1,2,1,3,1,1,0,0,3,1,1,1,2,0,3,2,0,1,1,2,3,3,3,0,3,0,0,2,0,3,
|
||||
3,2,0,0,3,2,3,1,2,3,0,3,2,0,1,2,2,2,0,2,0,1,2,2,3,1,2,2,1,1,1,1,
|
||||
},
|
||||
{0,2,3,4,5,7},
|
||||
{2,7}
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
0,1,2,0,3,3,0,3,2,1,3,3,0,3,1,1,3,2,3,2,3,0,0,0,3,0,2,2,3,2,2,3,
|
||||
2,2,3,1,2,3,1,2,0,3,0,2,3,1,0,0,3,2,1,2,1,2,1,3,1,0,2,3,3,1,3,2,
|
||||
},
|
||||
{2,3,4,5,6,7},
|
||||
{4,6}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const int naomi_m2_board::fn1_game_key_scheduling[38][2] = {
|
||||
{1,29}, {1,71}, {2,4}, {2,54}, {3,8}, {4,56}, {4,73}, {5,11},
|
||||
{6,51}, {7,92}, {8,89}, {9,9}, {9,10}, {9,39}, {9,41}, {9,58},
|
||||
{9,59}, {9,86}, {10,90}, {11,6}, {12,64}, {13,49}, {14,44}, {15,40},
|
||||
{16,69}, {17,15}, {18,23}, {18,43}, {19,82}, {20,81}, {21,32}, {21,61},
|
||||
{22,5}, {23,66}, {24,13}, {24,45}, {25,12}, {25,35}
|
||||
};
|
||||
|
||||
const int naomi_m2_board::fn2_game_key_scheduling[34][2] = {
|
||||
{0,0}, {1,3}, {2,11}, {3,20}, {4,22}, {5,23}, {6,29}, {7,38},
|
||||
{8,39}, {9,47}, {9,55}, {9,86}, {9,87}, {9,90}, {10,50}, {10,53},
|
||||
{11,57}, {12,59}, {13,61}, {13,64}, {14,63}, {15,67}, {16,72}, {17,83},
|
||||
{18,88}, {19,94}, {20,35}, {21,17}, {21,92}, {22,6}, {22,11}, {23,85},
|
||||
{24,16}, {25,25}
|
||||
};
|
||||
|
||||
const int naomi_m2_board::fn1_sequence_key_scheduling[20][2] = {
|
||||
{0,52}, {1,34}, {2,17}, {3,36}, {4,84}, {4,88}, {5,57}, {6,48},
|
||||
{6,68}, {7,76}, {8,83}, {9,30}, {10,22}, {10,41}, {11,38}, {12,55},
|
||||
{13,74}, {14,19}, {14,80}, {15,26}
|
||||
};
|
||||
|
||||
const int naomi_m2_board::fn2_sequence_key_scheduling[16] = {77,34,8,42,36,27,69,66,13,9,79,31,49,7,24,64};
|
||||
|
||||
const int naomi_m2_board::fn2_middle_result_scheduling[16] = {1,10,44,68,74,78,81,95,2,4,30,40,41,51,53,58};
|
||||
|
||||
int naomi_m2_board::feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys)
|
||||
UINT16 naomi_m2_board::read_callback(UINT32 addr)
|
||||
{
|
||||
int k,m;
|
||||
int aux;
|
||||
int result=0;
|
||||
if ((addr & 0xffff0000) == 0x01000000) {
|
||||
int base = 2*(addr & 0x7fff);
|
||||
return ram[base+1] | (ram[base] << 8);
|
||||
|
||||
for (m=0; m<4; ++m) { // 4 sboxes
|
||||
for (k=0, aux=0; k<6; ++k)
|
||||
if (sboxes[m].inputs[k]!=-1)
|
||||
aux |= BIT(input, sboxes[m].inputs[k]) << k;
|
||||
|
||||
aux = sboxes[m].table[(aux^subkeys)&0x3f];
|
||||
|
||||
for (k=0; k<2; ++k)
|
||||
result |= BIT(aux,k) << sboxes[m].outputs[k];
|
||||
|
||||
subkeys >>=6;
|
||||
}
|
||||
|
||||
return result;
|
||||
else {
|
||||
const UINT8 *base = m_region->base() + 2*addr;
|
||||
return base[1] | (base[0] << 8);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************
|
||||
This implementation is an "educational" version. It must be noted that it can be speed-optimized in a number of ways.
|
||||
The most evident one is to factor out the parts of the key-scheduling that must only be done once (like the game-key &
|
||||
sequence-key parts) as noted in the comments inlined in the function. More sophisticated speed-ups can be gained by
|
||||
noticing that the weak key-scheduling would allow to create some pregenerated look-up tables for doing most of the work
|
||||
of the function. Even so, it would still be pretty slow, so caching techniques could be a wiser option here.
|
||||
**************************/
|
||||
static MACHINE_CONFIG_FRAGMENT( naomim2 )
|
||||
MCFG_DEVICE_ADD("segam2crypt", SEGA315_5881_CRYPT, 0)
|
||||
MCFG_SET_READ_CALLBACK(naomi_m2_board, read_callback)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
UINT16 naomi_m2_board::block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data)
|
||||
machine_config_constructor naomi_m2_board::device_mconfig_additions() const
|
||||
{
|
||||
int j;
|
||||
int aux,aux2;
|
||||
int A,B;
|
||||
int middle_result;
|
||||
UINT32 fn1_subkeys[4];
|
||||
UINT32 fn2_subkeys[4];
|
||||
|
||||
/* Game-key scheduling; this could be done just once per game at initialization time */
|
||||
memset(fn1_subkeys,0,sizeof(UINT32)*4);
|
||||
memset(fn2_subkeys,0,sizeof(UINT32)*4);
|
||||
|
||||
for (j=0; j<38; ++j) {
|
||||
if (BIT(game_key, fn1_game_key_scheduling[j][0])!=0) {
|
||||
aux = fn1_game_key_scheduling[j][1]%24;
|
||||
aux2 = fn1_game_key_scheduling[j][1]/24;
|
||||
fn1_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
|
||||
for (j=0; j<34; ++j) {
|
||||
if (BIT(game_key, fn2_game_key_scheduling[j][0])!=0) {
|
||||
aux = fn2_game_key_scheduling[j][1]%24;
|
||||
aux2 = fn2_game_key_scheduling[j][1]/24;
|
||||
fn2_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
/********************************************************/
|
||||
|
||||
/* Sequence-key scheduling; this could be done just once per decryption run */
|
||||
for (j=0; j<20; ++j) {
|
||||
if (BIT(sequence_key,fn1_sequence_key_scheduling[j][0])!=0) {
|
||||
aux = fn1_sequence_key_scheduling[j][1]%24;
|
||||
aux2 = fn1_sequence_key_scheduling[j][1]/24;
|
||||
fn1_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
|
||||
for (j=0; j<16; ++j) {
|
||||
if (BIT(sequence_key,j)!=0) {
|
||||
aux = fn2_sequence_key_scheduling[j]%24;
|
||||
aux2 = fn2_sequence_key_scheduling[j]/24;
|
||||
fn2_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
|
||||
// subkeys bits 10 & 41
|
||||
fn2_subkeys[0] ^= (BIT(sequence_key,2)<<10);
|
||||
fn2_subkeys[1] ^= (BIT(sequence_key,4)<<17);
|
||||
/**************************************************************/
|
||||
|
||||
// First Feistel Network
|
||||
|
||||
aux = BITSWAP16(counter,5,12,14,13,9,3,6,4, 8,1,15,11,0,7,10,2);
|
||||
|
||||
// 1st round
|
||||
B = aux >> 8;
|
||||
A = (aux & 0xff) ^ feistel_function(B,fn1_sboxes[0],fn1_subkeys[0]);
|
||||
|
||||
// 2nd round
|
||||
B = B ^ feistel_function(A,fn1_sboxes[1],fn1_subkeys[1]);
|
||||
|
||||
// 3rd round
|
||||
A = A ^ feistel_function(B,fn1_sboxes[2],fn1_subkeys[2]);
|
||||
|
||||
// 4th round
|
||||
B = B ^ feistel_function(A,fn1_sboxes[3],fn1_subkeys[3]);
|
||||
|
||||
middle_result = (B<<8)|A;
|
||||
|
||||
|
||||
/* Middle-result-key sheduling */
|
||||
for (j=0; j<16; ++j) {
|
||||
if (BIT(middle_result,j)!=0) {
|
||||
aux = fn2_middle_result_scheduling[j]%24;
|
||||
aux2 = fn2_middle_result_scheduling[j]/24;
|
||||
fn2_subkeys[aux2] ^= (1<<aux);
|
||||
}
|
||||
}
|
||||
/*********************/
|
||||
|
||||
// Second Feistel Network
|
||||
|
||||
aux = BITSWAP16(data,14,3,8,12,13,7,15,4, 6,2,9,5,11,0,1,10);
|
||||
|
||||
// 1st round
|
||||
B = aux >> 8;
|
||||
A = (aux & 0xff) ^ feistel_function(B,fn2_sboxes[0],fn2_subkeys[0]);
|
||||
|
||||
// 2nd round
|
||||
B = B ^ feistel_function(A,fn2_sboxes[1],fn2_subkeys[1]);
|
||||
|
||||
// 3rd round
|
||||
A = A ^ feistel_function(B,fn2_sboxes[2],fn2_subkeys[2]);
|
||||
|
||||
// 4th round
|
||||
B = B ^ feistel_function(A,fn2_sboxes[3],fn2_subkeys[3]);
|
||||
|
||||
aux = (B<<8)|A;
|
||||
|
||||
aux = BITSWAP16(aux,15,7,6,14,13,12,5,4, 3,2,11,10,9,1,0,8);
|
||||
|
||||
return aux;
|
||||
return MACHINE_CONFIG_NAME( naomim2 );
|
||||
}
|
||||
|
||||
UINT16 naomi_m2_board::get_decrypted_16()
|
||||
{
|
||||
UINT16 enc;
|
||||
|
||||
if((prot_cur_address & 0xffff0000) == 0x01000000) {
|
||||
int base = 2*(prot_cur_address & 0x7fff);
|
||||
enc = ram[base+1] | (ram[base] << 8);
|
||||
} else {
|
||||
const UINT8 *base = m_region->base() + 2*prot_cur_address;
|
||||
enc = base[1] | (base[0] << 8);
|
||||
}
|
||||
|
||||
UINT16 dec = block_decrypt(key, subkey, prot_cur_address, enc);
|
||||
UINT16 res = (dec & 3) | (dec_hist & 0xfffc);
|
||||
dec_hist = dec;
|
||||
|
||||
prot_cur_address ++;
|
||||
return res;
|
||||
}
|
||||
|
||||
void naomi_m2_board::enc_start()
|
||||
{
|
||||
buffer_pos = BUFFER_SIZE;
|
||||
dec_header = get_decrypted_16() << 16;
|
||||
dec_header |= get_decrypted_16();
|
||||
|
||||
if(dec_header & FLAG_COMPRESSED) {
|
||||
line_buffer_size = dec_header & FLAG_LINE_SIZE_512 ? 512 : 256;
|
||||
line_buffer_pos = line_buffer_size;
|
||||
buffer_bit = 7;
|
||||
}
|
||||
enc_ready = true;
|
||||
}
|
||||
|
||||
void naomi_m2_board::enc_fill()
|
||||
{
|
||||
assert(buffer_pos == BUFFER_SIZE);
|
||||
for(int i = 0; i != BUFFER_SIZE; i+=2) {
|
||||
UINT16 val = get_decrypted_16();
|
||||
buffer[i] = val;
|
||||
buffer[i+1] = val >> 8;
|
||||
}
|
||||
buffer_pos = 0;
|
||||
}
|
||||
|
||||
/* node format
|
||||
0xxxxxxx - next node index
|
||||
1a0bbccc - end node
|
||||
a - 0 = repeat
|
||||
1 = fetch
|
||||
b - if a = 1
|
||||
00 - fetch 0
|
||||
01 - fetch 1
|
||||
11 - fetch -1
|
||||
if a = 0
|
||||
000
|
||||
c - repeat/fetch counter
|
||||
count = ccc + 1
|
||||
11111111 - empty node
|
||||
*/
|
||||
const UINT8 naomi_m2_board::trees[9][2][32] = {
|
||||
{
|
||||
{0x01,0x10,0x0f,0x05,0xc4,0x13,0x87,0x0a,0xcc,0x81,0xce,0x0c,0x86,0x0e,0x84,0xc2,
|
||||
0x11,0xc1,0xc3,0xcf,0x15,0xc8,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc7,0x02,0x03,0x04,0x80,0x06,0x07,0x08,0x09,0xc9,0x0b,0x0d,0x82,0x83,0x85,0xc0,
|
||||
0x12,0xc6,0xc5,0x14,0x16,0xca,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x02,0x80,0x05,0x04,0x81,0x10,0x15,0x82,0x09,0x83,0x0b,0x0c,0x0d,0xdc,0x0f,0xde,
|
||||
0x1c,0xcf,0xc5,0xdd,0x86,0x16,0x87,0x18,0x19,0x1a,0xda,0xca,0xc9,0x1e,0xce,0xff,},
|
||||
{0x01,0x17,0x03,0x0a,0x08,0x06,0x07,0xc2,0xd9,0xc4,0xd8,0xc8,0x0e,0x84,0xcb,0x85,
|
||||
0x11,0x12,0x13,0x14,0xcd,0x1b,0xdb,0xc7,0xc0,0xc1,0x1d,0xdf,0xc3,0xc6,0xcc,0xff,},
|
||||
},
|
||||
{
|
||||
{0xc6,0x80,0x03,0x0b,0x05,0x07,0x82,0x08,0x15,0xdc,0xdd,0x0c,0xd9,0xc2,0x14,0x10,
|
||||
0x85,0x86,0x18,0x16,0xc5,0xc4,0xc8,0xc9,0xc0,0xcc,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0x01,0x02,0x12,0x04,0x81,0x06,0x83,0xc3,0x09,0x0a,0x84,0x11,0x0d,0x0e,0x0f,0x19,
|
||||
0xca,0xc1,0x13,0xd8,0xda,0xdb,0x17,0xde,0xcd,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x80,0x0d,0x04,0x05,0x15,0x83,0x08,0xd9,0x10,0x0b,0x0c,0x84,0x0e,0xc0,0x14,
|
||||
0x12,0xcb,0x13,0xca,0xc8,0xc2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc5,0x02,0x03,0x07,0x81,0x06,0x82,0xcc,0x09,0x0a,0xc9,0x11,0xc4,0x0f,0x85,0xd8,
|
||||
0xda,0xdb,0xc3,0xdc,0xdd,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x80,0x06,0x0c,0x05,0x81,0xd8,0x84,0x09,0xdc,0x0b,0x0f,0x0d,0x0e,0x10,0xdb,
|
||||
0x11,0xca,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc4,0x02,0x03,0x04,0xcb,0x0a,0x07,0x08,0xd9,0x82,0xc8,0x83,0xc0,0xc1,0xda,0xc2,
|
||||
0xc9,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x02,0x06,0x0a,0x83,0x0b,0x07,0x08,0x09,0x82,0xd8,0x0c,0xd9,0xda,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc3,0x80,0x03,0x04,0x05,0x81,0xca,0xc8,0xdb,0xc9,0xc0,0xc1,0x0d,0xc2,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x02,0x03,0x04,0x81,0x07,0x08,0xd8,0xda,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc2,0x80,0x05,0xc9,0xc8,0x06,0x82,0xc0,0x09,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0x80,0x04,0xc8,0xc0,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc1,0x02,0x03,0x81,0x05,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
{
|
||||
{0x01,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
{0xc0,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
|
||||
},
|
||||
};
|
||||
|
||||
int naomi_m2_board::get_compressed_bit()
|
||||
{
|
||||
if(buffer_pos == BUFFER_SIZE)
|
||||
enc_fill();
|
||||
int res = (buffer[buffer_pos^1] >> buffer_bit) & 1;
|
||||
buffer_bit--;
|
||||
if(buffer_bit == -1) {
|
||||
buffer_bit = 7;
|
||||
buffer_pos++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void naomi_m2_board::line_fill()
|
||||
{
|
||||
assert(line_buffer_pos == line_buffer_size);
|
||||
UINT8 *lp = line_buffer;
|
||||
UINT8 *lc = line_buffer_prev;
|
||||
line_buffer = lc;
|
||||
line_buffer_prev = lp;
|
||||
line_buffer_pos = 0;
|
||||
|
||||
UINT32 line_buffer_mask = line_buffer_size-1;
|
||||
|
||||
for(int i=0; i != line_buffer_size;) {
|
||||
// vlc 0: start of line
|
||||
// vlc 1: interior of line
|
||||
// vlc 2-9: 7-1 bytes from end of line
|
||||
|
||||
int slot = i ? i < line_buffer_size - 7 ? 1 : (i & 7) + 1 : 0;
|
||||
|
||||
UINT32 tmp = 0;
|
||||
while (!(tmp&0x80))
|
||||
if(get_compressed_bit())
|
||||
tmp = trees[slot][1][tmp];
|
||||
else
|
||||
tmp = trees[slot][0][tmp];
|
||||
if(tmp != 0xff) {
|
||||
int count = (tmp & 7) + 1;
|
||||
|
||||
if(tmp&0x40) {
|
||||
// Copy from previous line
|
||||
|
||||
static int offsets[4] = {0, 1, 0, -1};
|
||||
int offset = offsets[(tmp & 0x18) >> 3];
|
||||
for(int j=0; j != count; j++) {
|
||||
lc[i^1] = lp[((i+offset) & line_buffer_mask)^1];
|
||||
i++;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Get a byte in the stream and write n times
|
||||
UINT8 byte;
|
||||
byte = get_compressed_bit() << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = (byte | get_compressed_bit()) << 1;
|
||||
byte = byte | get_compressed_bit();
|
||||
for(int j=0; j != count; j++)
|
||||
lc[(i++)^1] = byte;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define _NAOMIM2_H_
|
||||
|
||||
#include "naomibd.h"
|
||||
#include "315-5881_crypt.h"
|
||||
|
||||
#define MCFG_NAOMI_M2_BOARD_ADD(_tag, _key_tag, _eeprom_tag, _actel_tag, _irq_cb) \
|
||||
MCFG_NAOMI_BOARD_ADD(_tag, NAOMI_M2_BOARD, _eeprom_tag, _actel_tag, _irq_cb) \
|
||||
@ -11,12 +12,17 @@ class naomi_m2_board : public naomi_board
|
||||
{
|
||||
public:
|
||||
naomi_m2_board(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
UINT32 rom_cur_address;
|
||||
static const int RAM_SIZE = 65536;
|
||||
UINT8* ram;
|
||||
|
||||
static void static_set_tags(device_t &device, const char *_key_tag);
|
||||
UINT16 read_callback(UINT32 addr);
|
||||
|
||||
protected:
|
||||
virtual void device_start();
|
||||
virtual void device_reset();
|
||||
virtual machine_config_constructor device_mconfig_additions() const;
|
||||
|
||||
virtual void board_setup_address(UINT32 address, bool is_dma);
|
||||
virtual void board_get_buffer(UINT8 *&base, UINT32 &limit);
|
||||
@ -24,48 +30,12 @@ protected:
|
||||
virtual void board_write(offs_t offset, UINT16 data);
|
||||
|
||||
private:
|
||||
enum {
|
||||
RAM_SIZE = 65536, BUFFER_SIZE = 32768, LINE_SIZE = 512,
|
||||
FLAG_COMPRESSED = 0x10000, FLAG_LINE_SIZE_512 = 0x20000
|
||||
};
|
||||
|
||||
|
||||
const char *key_tag;
|
||||
UINT32 key;
|
||||
|
||||
UINT8 *ram, *buffer, *line_buffer, *line_buffer_prev;
|
||||
UINT32 rom_cur_address, prot_cur_address;
|
||||
UINT16 subkey, dec_hist;
|
||||
UINT32 dec_header;
|
||||
bool enc_ready;
|
||||
|
||||
int buffer_pos, line_buffer_pos, line_buffer_size, buffer_bit;
|
||||
|
||||
struct sbox {
|
||||
UINT8 table[64];
|
||||
int inputs[6]; // positions of the inputs bits, -1 means no input except from key
|
||||
int outputs[2]; // positions of the output bits
|
||||
};
|
||||
|
||||
static const sbox fn1_sboxes[4][4];
|
||||
static const sbox fn2_sboxes[4][4];
|
||||
|
||||
static const int fn1_game_key_scheduling[38][2];
|
||||
static const int fn2_game_key_scheduling[34][2];
|
||||
static const int fn1_sequence_key_scheduling[20][2];
|
||||
static const int fn2_sequence_key_scheduling[16];
|
||||
static const int fn2_middle_result_scheduling[16];
|
||||
|
||||
static const UINT8 trees[9][2][32];
|
||||
|
||||
int feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys);
|
||||
UINT16 block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data);
|
||||
|
||||
UINT16 get_decrypted_16();
|
||||
int get_compressed_bit();
|
||||
|
||||
void enc_start();
|
||||
void enc_fill();
|
||||
void line_fill();
|
||||
|
||||
required_device<sega_315_5881_crypt_device> m_cryptdevice;
|
||||
};
|
||||
|
||||
extern const device_type NAOMI_M2_BOARD;
|
||||
|
@ -572,6 +572,8 @@ UINT32 ffreveng_prot_read_callback( address_space &space, int protaddr, UINT32 k
|
||||
*
|
||||
*************************************/
|
||||
|
||||
// the naomi hookup of 315-5881 reads 16-bits at a time, here we seem to read 32?
|
||||
|
||||
READ32_MEMBER( stv_state::common_prot_r )
|
||||
{
|
||||
UINT32 *ROM = (UINT32 *)space.machine().root_device().memregion("abus")->base();
|
||||
@ -616,10 +618,18 @@ WRITE32_MEMBER ( stv_state::common_prot_w )
|
||||
else if(offset == 2)
|
||||
{
|
||||
COMBINE_DATA(&m_abus_prot_addr);
|
||||
|
||||
m_cryptdevice->set_addr_low(m_abus_prot_addr >> 16);
|
||||
m_cryptdevice->set_addr_high(m_abus_prot_addr&0xffff);
|
||||
|
||||
}
|
||||
else if(offset == 3)
|
||||
{
|
||||
COMBINE_DATA(&m_abus_protkey);
|
||||
|
||||
m_cryptdevice->set_subkey(m_abus_protkey);
|
||||
|
||||
|
||||
int a_bus_vector;
|
||||
a_bus_vector = m_abus_prot_addr >> 16;
|
||||
a_bus_vector|= (m_abus_prot_addr & 0xffff) << 16;
|
||||
|
@ -1707,6 +1707,7 @@ $(MAMEOBJ)/sega.a: \
|
||||
$(DRIVERS)/naomi.o $(MACHINE)/dc.o $(VIDEO)/powervr2.o $(MACHINE)/naomi.o \
|
||||
$(MACHINE)/naomig1.o $(MACHINE)/naomibd.o $(MACHINE)/naomirom.o $(MACHINE)/naomigd.o \
|
||||
$(MACHINE)/naomicrypt.o $(MACHINE)/naomim1.o $(MACHINE)/naomim2.o $(MACHINE)/naomim4.o \
|
||||
$(MACHINE)/315-5881_crypt.o \
|
||||
$(MACHINE)/awboard.o \
|
||||
$(MACHINE)/mie.o $(MACHINE)/maple-dc.o $(MACHINE)/mapledev.o $(MACHINE)/dc-ctrl.o $(MACHINE)/jvs13551.o \
|
||||
$(DRIVERS)/triforce.o \
|
||||
|
Loading…
Reference in New Issue
Block a user