Skip to content
This repository has been archived by the owner on Dec 27, 2018. It is now read-only.

psx pseudo docs

Pedro edited this page Sep 12, 2016 · 8 revisions

PSX SotN Know Types and Code

This page objective is to help developers in better understanding SotN resources and in no-way is a documentation to any code on this or any other project.

Note: This is based on Nyxojaele's papers and hasn't yet be tested.


Support Types, Structures and Arrays

Before entering these lands you might understand a few items:

  • uXX is an unsigned integer sized in XX bits
  • uXX_le is an unsigned integer sized in XX bits in little-endian
  • YY[XX] is an array of XX occurrences of YY
  • YY[VARIABLE] is an array of unknown occurrences of YY
  • {...} means it's a structure
  • endianness key means the structure as a whole and not each of its elements
  • If endianness key is not specified consider it big-endian
  • XX_ptr<YY> is a pointer to something of type YY

2d_bigpos

total-size: 4 Bytes
type: {u16_le x, u16_le y}

2d_bigsize

total-size: 4 Bytes
type: {u16_le width, u16_le height}

2d_pos

total-size: 2 Bytes
type: {u8 x, u8 y}

2d_resize

total-size: 3 Bytes
endianness: le
type: {
	u6 src_width,
	u6 src_height,
	u6 dst_width,
	u6 dst_height,
}

2d_size

total-size: 2 Bytes
type: {u8 width, u8 height}

2d_smallpos

total-size: 1 Byte
type: {u4 x, u4 y}

2d_smallpos_16

total-size: 1 Byte
type: 2d_smallpos

Not real position, multiply each element by 16 to get real position

2d_smallpos_256

total-size: 1 Byte
type: 2d_smallpos

Not real position, multiply each element by 256 to get real position

clut

total-size: 32 Bytes
type: rgba[16]

clut_coord_index

total-size: 1 Byte
type: u8

clut_index

total-size: 4 bits
type: u4

collision_type

total-size: 1 Byte
type: enum

entity_code_sprite

total-size: (>= 6 Bytes)
type {
	entity_sprite_partref ref,
	union {
		entity_sprite_part[VARIABLE] parts,
		2d_big_pos dest_pos
	}
}

if ref.is_mapped then parts exists else dest_pos exists

entity_sprite_partref

total-size: 2 Byte
endianness: le
type {
	u1 is_mapped,
	union {
		u15 count
		u15 index
	}
}

if is_mapped then count will have the number of entity_code_sprite.parts else you'll have to find a generic part through the index value

entity_code_state

total-size: 2 Byte
endianness: le

entity_ai_state

total-size: 2 Byte
endianness: le

entity_def

total-size: 10 bytes
type: {
	2d_bigpos pos,
	entity_identity identity,
	entity_state state,
	entity_ai_state ai_state
}

entity_gfxdef

total-size: (>= 8 Bytes)
type: entity_gfxpkg[VARIABLE]

entity_gfxentry

total-size: 8 Bytes
type: {
	2d_pos vram_pos,
	2d_size dim,
	psx_ptr data
}

data is compressed

entity_gfx_index

total-size: 1 Byte
type: u8
	

TODO: Is this actually an entity_gfxentry_index or an entity_gfxpkg_index?

entity_gfxpkg

total-size: (>= 8 Bytes)
type: {
	u32 start,
	entity_gfxentry[VARIABLE] entries
	u32 end
}
  • start is 0x00000000 (generally) or 0x00000004 (when last package)
  • end is always 0xFFFFFFFF
  • TODO: is start little-endian?

entity_identity

total-size: 2 Byte
endianness: le
type: {
	u3 unknow_a,
	u3 unknow_b,
	u10 type_id
}

entity_layout_index

total-size: 1 Byte
type: u8

entity_sprite_part

total-size: 22 Bytes
type: {
	u16_le draw_flags,
	2d_bigpos dest_pos,
	2d_bigsize dest_dim,
	u16_le clut,
	u16_le texture_pg,
	2d_bigpos src_b_pos,
	2d_bigpos src_e_pos,
}

entity_state

total-size: 2 Byte
endianness: le
type: {
	u3 death_id,
	u9 unknow,
	u4 type_id
}

map

total-size: (>= 64 Bytes)
type: {
	map_header sections,
	u8[VARIABLE] data
}

data includes 2 map_lsentity per room which can be founded in-file positions got from these values:

  • X-sorted version: *((u16*)(entity_spawnroom + 28))
  • Y-sorted version: *((u16*)(entity_spawnroom + 40))

map_header

total-size: 64 Bytes
type: map_intern_ptr[16]
/*
	Index	Offset	Type
	4		0x10	map_lsroom
	8		0x20	map_lslayout
	?		0x??	entity_gfxdef
	?		0x??	entity_spawnframe
	?		0x??	entity_spawnroom
	?		0x??	sprite_banks

map_intern_ptr

total-size: 4 Bytes
type: psx_ptr
	

You can always subtract 0x80180000 to get the in-file position

map_layer

total-size: 16 Bytes
type: {
	map_intern_ptr<tile_index[VARIABLE]> tiles,
	map_intern_ptr<tiledef> tiles_def,
	2d_resize dims,
	u8 tile_flags,
	u8 z_index,
	u8 z_priority,
	u16_le layer_flags
}

map_layout

total-size: 8 Bytes
type: {
	map_intern_ptr<map_layer> foreground,
	map_intern_ptr<map_layer> background
}

map_layout_index

total-size: 1 Byte
type: u8

map_lsentity

total-size: (>= 8 Bytes)
type: {entity_def[VARIABLE] entities}

This array ends with a 0xFFFFFFFF

map_lslayout

total-size: (>= 8 Bytes)
type: {map_layout[VARIABLE] layouts}

map_lsroom

total-size: (>= 9 Bytes)
type: {
	map_room[VARIABLE] rooms,
	u24 padding
}
  • This array ends with a 0x40
  • padding is always zeroed

map_room

total-size: 8 Bytes
type: {
	2d_pos map_start,
	2d_pos map_end,
	map_layout_index layout_id,
	u8 yet_unknow,
	entity_gfx_index ent_gfx_id,
	entity_layout_index ent_layout_id
}

psx_ptr

total-size: 4 Bytes
type: u32_le

rgba

total-size: 2 Bytes
endianness: le
type: {u5 red, u5 green, u5 blue, u1 alpha}

tiledef

total-size: 16 Bytes
type: {
	psx_ptr<2d_smallpos[VARIABLE]> tileset_coords,
	psx_ptr<2d_smallpos[VARIABLE]> tile_coords,
	psx_ptr<clut_coord_index[VARIABLE]> clut_coords,
	psx_ptr<collision_type[VARIABLE]> collisions
}

tile_index

total-size: 2 Bytes
type: u16_le

tileset

total-size: 262144 Bytes
type: tileset_slot[8]

tileset_group

total-size: 8192 Bytes
type: tileset_tile[8][8]

tileset_slot

total-size: 32768 Bytes
type: tileset_group[4]

tileset_tile

total-size: 128 Bytes
type: clut_index[16][16]

Algorithms

decompress

fn(in, out)
	common = in.read_le(8)
	.loop:
		opcode = in.read_nibble(1)
		.switch(opcode):
			.case 0:
				out.write(repeat("0, ((in.read_nibble(1) << 4) + in.read_nibble(1) + 19)))
			.case 0x1:
				out.write(in.read_nibble(1))
			.case 0x2:
				out.write(repeat(in.read_nibble(1), 2))
			.case 0x3:
				out.write(in.read_nibble(2))
			.case 0x4:
				out.write(in.read_nibble(3))
			.case 0x5:
				desired = in.read_nibble(1)
				loop = in.read_nibble(1)
				out.write(desired.repeat(loop))
			.case 0x6:
				out.write(repeat("0", in.read_nibble(1)))
			.case 0x7..0xE:
				index = opcode-0x7
				target = common[index]
				loop = (target & 0xf0) >> 4
				desired = target & 0x0
				out.write(desired.repeat(loop))
			.case 0xF:
				.break
  • in and out would be like buffers
  • read_le(n) reads n Bytes in little-endian
  • read_nibble(n) reads n Nibbles
  • write(values) writes values (remember: Nibbles are the smallest value)
  • repeat(data, n) repeat data n times
  • "0 is a zeroed Nibble