An XP3 file begins with an 11-byte magic number, followed by an 8-byte offset from the start of the header (usually the start of the file), info_offset. All numbers are little-endian.
offset
length
contents
notes
0x00
11
magic
XP3\x0D\x0A\x20\x0A\x1A\x8B\x67\x01
0x0B
8
info_offset
0x??
??
file_data
info_offset
17 + compressed_size
file_info
At info_offset there are two possibilities. The first byte may be 0x80 (in case of a file generated by KiriKiri Z), in which case skip 8 bytes and read the true info offset. Jump there and continue.
info_offset + x
length
contents
notes
0x00
1
version
0x80
0x01
8
???
unknown
0x09
8
info_offset
true info offset
Otherwise, the first byte should be 0x01:
info_offset + x
length
contents
notes
0x00
1
version
0x01
0x01
8
compressed_size
0x09
8
original_size
0x0A
compressed_size
file_table
if compressed, decompress with zlib
The end of the file_table should be the end of the file.