Skip to content

The Headers

Attacktive edited this page Jul 2, 2022 · 11 revisions

KTSR Header

Based on the document

Header Chunk (64 bytes)

4 bytes (char)   // chunk magic: "KTSR" (fixed)
4 bytes (uint32) // chunk type: "0x02 0x94 0xDD 0xFC" (fixed)
1 bytes (uint16) // version(?): "1" (have seen nothing else so far)
2 byte           // nulls (fixed)
1 byte (uint8)   // platform: 0x01 - PC, 0x03 - PS Vita, 0x04 - Switch
4 bytes          // game ID
8 bytes          // nulls (fixed)
4 bytes (uint32) // file size
4 bytes (uint32) // file size (repeated)
32 bytes         // nulls (fixed)

Media

Header (32 bytes)

4 bytes (uint32)  // config(?): "0x09 0xD4 0xF4 0x15" (fixed) … ①
4 bytes (uint32)  // total size of the entry including ①: "0x61 0x72 0xDB 0xA8" … ②
4 bytes (uint32)  // unknown: "0xC9 0xC4 0x8E 0xC1" (varies per entry)
4 bytes (uint32)  // config(?): "0x20 0x00 0x00 0x00" (fixed?)
4 bytes (uint32)  // size of sound data: "0xC5 0xCC 0xCB 0x70" … ③
12 bytes (uint32) // nulls (fixed)

Body (first 8 bytes)

4 bytes (uint32)  // media type: "KOVS"
4 bytes (uint32)  // ③ - 32 (0x20)

KTSR Specification

Click to expandCONTENTS IN LITTLE-ENDIAN MAIN KTSR HEADER
0x00: Magic word KTSR
0x04: 0x1A487B77
0x08: 0x0100
0x0A: Console format: 0x0001 for PC, 0x0203 for PSVita
0x0C: 0x4ACAC960
0x10: 0x00
0x18: Entire file size
0x1C: Entire file size
0x20: 0x00

KTSR INDIVIDUAL SOUND HEADERS STARTING AT 0x40 GOES EACH PIECE OF THE HEADER, VARIABLE SIZE THE CONTENTS OF THIS PART SEEM TO BE ALMOST EXACTLY THE SAME BETWEEN CONSOLES (EVEN WITH THE SAMPLE RATE VARIATIONS), SO THERE'S A CHANCE THERE'S NO NEED TO CHANGE THIS AREA.

FIRST SECTION

0x04 bytes: 0x368C88BD
0x04 bytes: Entire header size (Usually 0xF0 or 0x1E0)
(Ignore the rest of the piece)

CONTINUE WITH ONE PIECE OF HEADER PER FILE, THEN PROCEED TO ACTUAL SOUND FILES.

SOUND FILE:

FIRST SECTION

0x04 bytes: 0x70CBCCC5
0x04 bytes: Entire sound file size
0x04 bytes: Unknown variable data. Might be related with miliseconds/sound size content.
0x04 bytes: 0x00
0x04 bytes: 0x01
0x04 bytes: Pointer to the pointer to the second section, after the sound name (Variable)
0x04 bytes: Position of the sound name (Variable)
0xXX bytes: Sound name (ASCII)
0x04 bytes: Pointer to the second section (ALWAYS ALIGNED TO 0x08 BYTES)

SECOND SECTION

0x04 bytes: Unknown variable data (0xF973F26F or 0x4EBDFD41)
0x04 bytes: File size (Starting at the beginning of the block)
0x04 bytes: Unknown variable data (0xD2DB2960 or 0x09F76FDC)
0x04 bytes: 0x01. Might be the number of channels.
0x04 bytes: Unknown variable data. Usually 0x1000. Might be the block align.
0x04 bytes: Sample rate
0x04 bytes: Sample number (One file had a higher amount than the real data, another one had the exact one. Might be rounded?)
0x04 bytes: Unknown variable data. Might be related with platform (PC has 0x00, VITA has 0x0100)
0x04 bytes: 0xFFFFFFFF
0x04 bytes: Unknown variable data.
0x04 bytes: Pointer to the format identifier (Starting at the beginning of the second section, ALIGNED TO 0x08 BYTES)
0x04 bytes: Pointer to the sound contents position value (Starting at the beginning of the second section)
0x04 bytes: Pointer to the sound contents size value (Starting at the beginning of the second section)
FILL WITH 0x00 UNTIL THE NEXT PART IS ALIGNED TO 0x08 bytes
0x04 bytes: Some sort of format identifier (0x01060200 for PC, 0x00A00400 in VITA)
 - IF THE FORMAT IDENTIFIER IS FROM VITA:
 - 0x04 bytes: Bitrate information (The one found at the 0x40 position in the RIFF header of an AT9 file):
   - 0x01 byte: 0xFE
   - 0x01 byte: 
        - 0x70 = 1 channel
        - 0x74 = 2 channels
   - 0x01 byte:
        - 0x02 = 36 kbps, byte rate 4500, audio block align 96
        - 0x03 = 48 kbps, byte rate 6000, audio block align 128
        - 0x04 = 60 kbps, byte rate 7500, audio block align 160
        - 0x05 = 72 kbps, byte rate 9000, audio block align 192
        - 0x06 = 84 kbps, byte rate 10500, audio block align 224
        - 0x07 = 96 kbps, byte rate 12000, audio block align 256
        - 0x09 = 120 kbps, byte rate 15000, audio block align 320
        - 0x0B = 144 kbps, byte rate 18000, audio block align 384
        - 0x0D = 168 kbps, byte rate 21000, audio block align 448
        - 0x0E = 192 kbps, byte rate 24000, audio block align 512
   - 0x01 byte: 0xF0
0x04 bytes: Sound contents position (Starting at the beginning of the second section, ALIGNED TO 0x10 BYTES)
0x04 bytes: Sound contents size
FILL WITH 0x00 UNTIL THE NEXT PART IS ALIGNED TO 0x10 bytes
0xXX bytes: Sound contents
Click to Expand
//------------------------------------------------
//--- 010 Editor v9.0.2 Binary Template
//
//      File: KTSR.bt
//   Authors: DeathChaos, 0Liam, Raytwo
//   Version: 1.04
//   Purpose: Parse KTSR files
//   Category: File Format
//   File Mask: *.ktsl2stbin, *.bin
//   ID Bytes: KTSR
//   History:
//   1.00    2019-08-05  DeathChaos - started this
//   1.01    2019-08-06  DeathChaos - Minor Code Cleanup
//   1.02    2019-08-19  0Liam      - Added support for other KTSR types and refactored code
//   1.03    2019-11-23  Raytwo     - Added information about some sections
//   1.04    2020-01-08  0Liam      - Updated for consistency
//------------------------------------------------

LittleEndian();
DisplayFormatHex();

struct {
	char magic[0x4] <name="Magic">;
	uint32 type_id <name="Type ID">;
	uint16 flags <name="Flags">; // If the value is not 1, the file will not load
	uint16 console_type <name="Console Type">; // 0x400 = Switch
	uint32 game_id <name="Game ID">;
	uint64 padding_1;
	uint32 decompressed_size <name="Decompressed Size">; // Assumed
	uint32 compressed_size <name="Compressed Size">; // Assumed
} ktsr <bgcolor=cYellow, size=0x40>;

typedef struct GCADPCMSTREAM
	{
        local int locptr = FTell();
		uint sectionMagic<name="Magic", format=hex>;
		uint sizeof_AudioFile<name="Audio Data size (Total File Size - 0x20)",format=hex>;
		uint fileLinkID<name="File Link ID",format=hex>;
		uint channels<name="Number of Channels">;
		uint unk_0x30;
		uint sample_rate<name="Sample Rate">;
		uint sound_duration<name="Duration = numOfSamples - (numOfSamples/0x8)">; // thanks KT
		uint unk_0x3c;
		int alwaysFF<name="Always 0xFFFFFFFF">;
		uint unk_0x44;
		uint dsp_info_pointer;
		uint dsp_info_size;
		uint stream_pointer_table_pointer;
		uint stream_size_table_pointer;
        FSeek(locptr + dsp_info_pointer);
        struct DSP_Info
        {
		    uint sound_duration<name="Duration = numOfSamples - (numOfSamples/0x8)">; // thanks KT
		    uint sampleCount;
		    uint sample_rate<name="Sample Rate">;
		    uint unk_0x10;
		    uint unk_0x14;
		    uint clippedSampleCount;
		    uint unk_0x1c;
		    ushort coef[8 * 2]<name="DSP Coefficient">;
		    byte KTPadding[0x24]<name="Padding">;
        }dsp_info[channels];
        FSeek(locptr + stream_pointer_table_pointer);
        int stream_pointers[channels];
        FSeek(locptr + stream_size_table_pointer);
        int stream_sizes[channels];
        local int j = 0;
        for(j = 0; j < channels; ++j) {
            FSeek(locptr + stream_pointers[j]);
            struct Stream_Data {
                byte stream[stream_sizes[j]];
            } stream<optimize=false>;
        }
	} GCADPCMStream;

struct SECTION {
	uint32 type_id <name="Type ID">;
	uint32 section_size <name="Section Size">;
	if (type_id == 0x15F4D409) { // // ktss_section (inside ktsl2stbin)
		uint32 link_id <name="Link ID">;
		uint32 unknown_1;
		uint32 kns_size <name="KNS Size">;
		ubyte padding_1[0x2C];
	} else if (type_id == 0x368C88BD) { // info_section (inside ktsl2asbin)
		uint32 link_id <name="Link ID">;
		uint16 channel_count <name="Channel Count">;
		uint16 layer_count <name="Layer Count">;
		uint32 padding_1;
		uint32 cancel <name="Cancel">;
		ubyte unknown_1[section_size - 0x18];
	} else if (type_id == 0xA8DB7261) { // padding_section (inside ktsl2asbin)
		ubyte padding_1[section_size - 0x8];
	} else if (type_id == 0x70CBCCC5 && section_size == 0x60) // info2_section (inside ktsl2asbin, linked to ktsl2stbin)
	{
		uint32 link_id <name="Link ID">;
		ubyte unknown_1[0x10];
		uint32 header_size <name="Header Size">;
		uint32 type_id_2 <name="Type ID 2">;
		uint32 section_size_2 <name="Section Size 2">;
		uint32 unknown_2;
		uint32 channel_count <name="Channel Count">;
		uint32 transition_related <name="Transition Related">;
		uint32 unknown_3;
		uint32 sample_rate <name="Sample Rate">;
		uint32 sample_count <name="Sample Count">;
		uint32 unknown_4;
		uint32 loop_start <name="Loop Start">;
		ubyte unknown_5[0xC];
		uint32 ktss_offset <name="KTSS Offset">;
		uint32 ktss_size <name="KTSS Size">;
		uint32 unknown_6;
	} else { // unknown (type ID not seen before)
		uint32 link_id <name="Link ID">;
		ubyte unknown_1[section_size - 0xC];
	};
};

if (ktsr.type_id == 0xFCDD9402) { // ktsl2stbin
	local TFindResults ktss = FindAll("KTSS");
	struct {
		SECTION ktss_section <name="KTSS Section", bgcolor=cRed>;
		char magic[0x4] <name="Magic">;
		uint32 section_size <name="Section Size">;
		ubyte padding_1[0x20 - FTell() % 0x20];
		ubyte codec_id <name="Codec ID">;
		ubyte unknown_1;
		ubyte unknown_2;
		ubyte unknown_3;
		uint32 codec_start_offset <name="Codec Start Offset">;
		ubyte layer_count <name="Layer Count">;
		ubyte channel_count <name="Channel Count">;
		uint16 unknown_4;
		uint32 sample_rate <name="Sample Rate", comment="The frequency at which the KTSS is read">;
		uint32 sample_count <name="Sample Count">;
		uint32 loop_start_sample <name="Loop Start Sample", comment="In sample block">;
		uint32 loop_length <name="Loop Length", comment="In sample block">;
		uint32 padding_2;
		uint32 audio_offset <name="Audio Offset">;
		uint32 audio_size <name="Audio Size">;
		uint32 unknown_5;
		uint32 packet_count <name="Packet Count">;
		uint16 packet_size <name="Packet Size">;
		uint16 unknown_6 <comment="Always 0x3C0 so far">;
		uint32 input_sample_rate <name="Input Sample Rate", comment="The frequency the original file was using">;
		uint16 skip <name="Skip", comment="Initial delay for complicated audio shit">;
		ubyte stream_count <name="Stream Count">;
		ubyte coupled_count <name="Coupled Count">;
		ubyte channel_mapping_family[channelCount] <name="Channel Mapping Family">;
		ubyte padding_3[0x70 - FTell() % 0x70];
		struct {
			BigEndian();
			uint32 size <name="Size">;
			LittleEndian();
			uint32 unknown_1;
			ubyte content[size] <name="Content">;
		} packet[packet_count] <name="Packet", optimize=false>;
	} ktss[ktss.count] <name="KTSS", optimize=false>;
} else if (ktsr.type_id == 0x1A487B77) { // ktsl2asbin
	local TFindResults info = FindAll(0x368C88BD);
	local TFindResults padding = FindAll(0xA8DB7261);
	local TFindResults info2 = FindAll(0x70CBCCC5);
	SECTION info_section[info.count] <name="Info Section", optimize=false, bgcolor=cRed>;
	SECTION padding_section[padding.count] <name="Padding Section", optimize=false, bgcolor=cBlue>;
	SECTION info2_section[info2.count] <name="Info2 Section", optimize=false, bgcolor=cGreen>;
} else if (ktsr.type_id == 0x9EE638C6) { // ktsl2gcbin - only tested on GlobalConfig.ktsl2gcbin, there may be more sections
	local TFindResults unknown1 = FindAll(0xE0736214);
	local TFindResults unknown2 = FindAll(0x009E4D89);
	local TFindResults unknown3 = FindAll(0x3CC43F64);
	local TFindResults unknown4 = FindAll(0xFDBC6FE2);
	local TFindResults unknown5 = FindAll(0x6D8C8F46);
	local TFindResults unknown6 = FindAll(0xBFC43B5D);
	SECTION unknown1_section[unknown1.count] <name="Unknown1 Section", optimize=false, bgcolor=cRed>;
	SECTION unknown2_section[unknown2.count] <name="Unknown2 Section", optimize=false, bgcolor=cBlue>;
	SECTION unknown3_section[unknown3.count] <name="Unknown3 Section", optimize=false, bgcolor=cGreen>;
	SECTION unknown4_section[unknown4.count] <name="Unknown4 Section", optimize=false, bgcolor=cPurple>;
	SECTION unknown5_section[unknown5.count] <name="Unknown5 Section", optimize=false, bgcolor=cLtRed>;
	SECTION unknown6_section[unknown6.count] <name="Unknown6 Section", optimize=false, bgcolor=cLtBlue>;
	ubyte padding_1[0x10 - FTell() % 0x10] <bgcolor=cLtGreen>;
};
Clone this wiki locally