├── LICENSE ├── README.md ├── ddr ├── ssq.md ├── step.md └── step2.md ├── generic └── tim.md ├── iidx ├── 1.md └── images │ └── catastrophe-iidxtiming.png └── ios ├── gen.md └── jbt-rb-orb.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 DJSLACKERS 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions may not be sold, nor may they be used in a commercial 8 | product or activity. 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rhythm Game Formats 2 | 3 | Ever wondered what makes your favorite rhythm game series tick? Now you know. 4 | This repository contains information about file formats used by the Bemani 5 | games we all know and love. 6 | 7 | ## Contributions 8 | 9 | Your help to fill in what's missing would be greatly appreciated! If you have 10 | information that can fill in the blanks or correct something that's already 11 | included in this repo, feel free to fork it and send a pull request. 12 | 13 | PRs will be verified by SaxxonPike before merge to ensure the quality of the 14 | repo. 15 | -------------------------------------------------------------------------------- /ddr/ssq.md: -------------------------------------------------------------------------------- 1 | # Dance Dance Revolution SSQ Format 2 | 3 | This document serves to explain the SSQ format used by Konami's Dance Dance 4 | Revolution series of video games. 5 | 6 | ## About this format 7 | 8 | To date, SSQ is the most recent format used by Konami's Dance Dance Revolution 9 | series. Its earliest occurrence is in unreferenced files in DDR 3rd Mix Plus. 10 | They are first officially used in DDR 4th Mix. 11 | 12 | SSQ appears to have been originally developed without freeze notes in mind. 13 | The reasoning is how early the format has been found and the hacky way it 14 | was implemented. Shock arrows are also implemented in a hacky way, although 15 | in a less intrusive manner (see below.) 16 | 17 | It supports BPM changes and multiple variable sized chunks, much like a WAV 18 | file. 19 | 20 | There is no indication how long an SSQ file actually is. The typical method is 21 | to keep reading until a value of zero is read for the length. Thus, each 22 | SSQ file ends with four 0x00 bytes. 23 | 24 | Chunks are `dword` aligned, meaning if the data length is not a multiple of 25 | four before being written, it is padded up to the next multiple of four. 26 | 27 | ## In a nutshell 28 | 29 | SSQ is a series of "chunks". Each chunk has a header describing what it is 30 | along with some chunk-specific metadata. 31 | 32 | Often, you'll find at least three chunks. They are the tempo data, some 33 | unknown yet consistent data, and the note data. One chunk exists per chart 34 | difficulty. Additional data is likely video scripting data; see below for 35 | details. 36 | 37 | ## Chunk format 38 | 39 | ``` 40 | Offset(h) Type Length Descrption 41 | +00 int32 4 length of the data in bytes including this header 42 | +04 int16 2 parameter 1: type of chunk 43 | +06 int16 2 parameter 2: type-dependent metadata 44 | +08 int16 2 parameter 3: type-dependent metadata 45 | +0A int16 2 parameter 4: type-dependent metadata 46 | +0C ? ? the rest of the data 47 | ``` 48 | 49 | ## Chunk types 50 | 51 | The format changes depending on parameter 1 found above. Data will have all its 52 | time offsets first, then all its corresponding data second. 53 | 54 | ### Type 01: tempo changes 55 | 56 | - Parameter 2: Time frames per second 57 | - Parameter 3: Number of BPM change/stop entries 58 | 59 | After the time offsets, tempo data is `int32` type and will be 4 bytes per 60 | entry. The time offset determines what point the song needs to change tempo 61 | or stop, and the data determines how many time frames are supposed to have 62 | passed. 63 | 64 | You can use the following formula to convert this kind of time table into BPM. 65 | Note that `i` must be at least 1 because deltas are used. 66 | 67 | ``` 68 | DeltaOffset = TimeOffset[i] - TimeOffset[i - 1]; 69 | DeltaTicks = TempoData[i] - TempoData[i - 1]; 70 | TicksPerSecond = Parameter2; 71 | MeasureLength = 4096; 72 | 73 | BPM = (DeltaOffset / MeasureLength) / ((DeltaTicks / TicksPerSecond) / 240); 74 | ``` 75 | 76 | Stops will be encoded as two consecutive entries with the same time offset, but 77 | different data. You can use the following formula to convert this kind of 78 | time into seconds. 79 | 80 | ``` 81 | DeltaTicks = TempoData[i] - TempoData[i - 1]; 82 | TicksPerSecond = Parameter2; 83 | 84 | StopLengthInSeconds = DeltaTicks / TicksPerSecond; 85 | ``` 86 | 87 | ### Type 02: unknown 88 | 89 | - Parameter 3: Number of entries 90 | 91 | After the time offsets, this data is `int16` type and will be 2 bytes per 92 | entry. Not much else is known about this chunk type. It appears in every 93 | observed SSQ file so far. It could possibly be linked to what tells the game 94 | when to end the song and show the results screen. 95 | 96 | The observed pattern in hex: 97 | ``` 98 | 0104 99 | 0201 100 | 0202 101 | 0205 102 | 0203 103 | 0204 104 | ``` 105 | 106 | ### Type 03: steps 107 | 108 | - Parameter 2: Chart difficulty type 109 | - Parameter 3: Number of steps 110 | 111 | After the time offsets, this data is `byte` type and will be at least one byte 112 | per entry (see below for details about why this varies.) 113 | 114 | #### Difficulty types 115 | 116 | Use Parameter 2 with the following table to determine the chart type. 117 | 118 | ``` 119 | Value(h) Type 120 | 0114 Single Basic 121 | 0214 Single Standard 122 | 0314 Single Heavy 123 | 0414 Single Beginner 124 | 0614 Single Challenge 125 | 126 | 0118 Double Basic 127 | 0218 Double Standard 128 | 0318 Double Heavy 129 | 0418 Double Beginner 130 | 0618 Double Challenge 131 | ``` 132 | 133 | #### Decoding steps 134 | 135 | Each arrow is represented by one bit of the data. Assuming the least 136 | significant bit is 0, and the most significant bit is 7, you can use this table 137 | to determine which arrow is to be pressed: 138 | 139 | ``` 140 | Bit Arrow 141 | 0 Player 1 Left 142 | 1 Player 1 Down 143 | 2 Player 1 Up 144 | 3 Player 1 Right 145 | 4 Player 2 Left 146 | 5 Player 2 Down 147 | 6 Player 2 Up 148 | 7 Player 2 Right 149 | ``` 150 | 151 | #### Decoding shock arrows 152 | 153 | As of DDR X, shock arrows are part of the format, and are indicated 154 | by having all bits set (a value of 0xFF). 155 | 156 | #### Decoding freeze arrows 157 | 158 | On DDR MAX and later mixes, it is possible to encounter data where the step 159 | data has no bits set (0x00). This indicates a special kind of arrow, such as 160 | a freeze. This special arrow data is present after all the normal data. 161 | 162 | The start of this data is dword-aligned after the end of all the normal 163 | step data above. 164 | 165 | Each time a value of `0x00` is encountered in the step data, it corresponds 166 | to a two-byte structure in the tacked-on extra data: 167 | 168 | ``` 169 | Offset(h) Type Length Descrption 170 | +00 byte 1 panels for the extra data 171 | +01 byte 1 type of the extra data 172 | ``` 173 | 174 | For freezes, the type should be `0x01`, and the panel data represents which 175 | panels are to be interpreted as starting a freeze. 176 | 177 | ### Type 04: background change script 178 | 179 | No parameters are known about this chunk type. It exists here for speculation 180 | only. 181 | 182 | ### Type 09: song metadata 183 | 184 | This section contains at least the title and artist of a song. This kind of chunk was discovered by looking at Dance Dance Revolution S+. Further details pending for this section. 185 | -------------------------------------------------------------------------------- /ddr/step.md: -------------------------------------------------------------------------------- 1 | # Dance Dance Revolution STEP Format 2 | 3 | This document serves to explain the STEP format used by Konami's Dance Dance 4 | Revolution series of video games. 5 | 6 | ## About this format 7 | 8 | STEP isn't the official name. It is, however, the format exported using DDR 9 | Utility. It is featured in 2nd Mix through 3rd Mix Plus, including Solo Bass 10 | Mix and Solo 2000. 11 | 12 | The Solo mixes have a slight variation in step panel configuration. See below 13 | for more information. 14 | 15 | ## In a nutshell 16 | 17 | TODO 18 | -------------------------------------------------------------------------------- /ddr/step2.md: -------------------------------------------------------------------------------- 1 | # Dance Dance Revolution STEP2 Format 2 | 3 | This document serves to explain the STEP2 format used by Konami's Dance Dance 4 | Revolution series of video games. 5 | 6 | ## About this format 7 | 8 | STEP2 isn't the official name. It is, however, the format exported using DDR 9 | Utility. It is featured only in 1st Mix and can quantize to 1/64. 10 | 11 | ## In a nutshell 12 | 13 | TODO 14 | -------------------------------------------------------------------------------- /generic/tim.md: -------------------------------------------------------------------------------- 1 | # Playstation TIM format 2 | 3 | This document serves to explain the TIM format used by Konami in a number of 4 | their rhythm games. The format is quite common for other Playstation games. 5 | 6 | ## About this format 7 | 8 | The TIM format appears to be very convenient to use for those who developed 9 | software for Sony's Playstation. It's no surprise this standard format was one 10 | chosen to be used for in-game visual assets in a number of Konami's rhythm 11 | games. 12 | 13 | This format does not include all the information needed to parse a TIM image 14 | fully; rather, it contains information that pertains only to the features used 15 | by Konami in their rhythm games. 16 | 17 | ## In a nutshell 18 | 19 | A TIM file consists of a header, possibly a palette (depending on the image 20 | pixel format), image transformation information, and raw pixel data. 21 | 22 | ## Header 23 | 24 | The first eight bytes determine if this is a TIM file, and what kind of data 25 | we can expect to follow. 26 | 27 | ``` 28 | Offset(h) Type Length Description 29 | 0 int32 4 File Identifier (0x00000010) 30 | 4 int32 4 Image type 31 | ``` 32 | 33 | ### Image types 34 | 35 | The image type in the header determines what data follows. Here is a table 36 | that should be used to determine if a palette is present, and what format the 37 | pixel data is in: 38 | 39 | ``` 40 | Value(h) BitDepth Palette? PixelFormat 41 | 00 4bpp No 4 bits per pixel, from an external palette 42 | 01 8bpp No 8 bits per pixel, from an external palette 43 | 02 16bpp No 16 bits per pixel, not indexed 44 | 03 24bpp No 24 bits per pixel, not indexed 45 | 08 4bpp Yes 4 bits per pixel, from a palette of 16 colors 46 | 09 8bpp Yes 8 bits per pixel, from a palette of 256 colors 47 | ``` 48 | 49 | ## Palette (CLUTs) 50 | 51 | If the bit depth is 4 or 8 bits per pixel, and does not use an external 52 | palette, a palette follows in the file. This palette, often called a CLUT (or 53 | Color LookUp Table) also has its own header. There can be multiple CLUTs, but 54 | this header will only appear once. 55 | 56 | ``` 57 | Offset(h) Type Length Description 58 | 0 int32 4 Length of all the CLUT data after the header 59 | 4 int16 2 Palette Origin X 60 | 6 int16 2 Palette Origin Y 61 | 8 int16 2 Colors per CLUT 62 | A int16 2 Number of CLUTs 63 | C ? ? CLUT data 64 | ``` 65 | 66 | If `ColorsPerCLUT` is zero, assume it's 16 for 4bpp images, and 256 for 8pp 67 | images. 68 | 69 | All colors are stored as 16 bits (two bytes) for each entry. To find out how 70 | many bytes you should have, use this formula: 71 | 72 | ``` 73 | CLUTDataLength = ColorsPerCLUT * NumberOfCLUTs * 2; 74 | ``` 75 | 76 | The CLUTs are stored one after another. Each 16-bit color can be decoded using 77 | the following method. Note that each color value will be in the range of 0 to 78 | 31: no intensity to full intensity for that particular color. 79 | 80 | ``` 81 | Red = ColorData & 0x1F; 82 | Green = (ColorData >> 5) & 0x1F; 83 | Blue = (ColorData >> 10) & 0x1F; 84 | Mask = ((ColorData >> 15) & 0x1) != 0; 85 | ``` 86 | 87 | The purpose of the mask color in Bemani games is unknown. It could possibly 88 | be used for transparency. 89 | 90 | ## Image metadata 91 | 92 | After the header and palette sections (if applicable), the image metadata 93 | section is present. 94 | 95 | ``` 96 | Offset(h) Type Length Description 97 | 0 int32 4 Length of pixel data after the header in bytes 98 | 4 int16 2 Image origin X 99 | 6 int16 2 Image origin Y 100 | 8 int16 2 Image stride* 101 | A int16 2 Image height 102 | C ? ? Raw image data 103 | ``` 104 | 105 | Image stride is the number of bytes required in internal memory per line of the 106 | image data. You can get the width of the image in pixels by using the following 107 | method. 108 | 109 | ``` 110 | Width = (Stride * 16) / BitsPerPixel; 111 | ``` 112 | 113 | ## Pixel data 114 | 115 | The pixel data itself is interpreted in a number of different ways based on 116 | the bits per pixel. 117 | 118 | ### 4bpp 119 | 120 | Each byte represents two pixels. The lower four bits make up the color index 121 | for the leftmost pixel, and the upper four bits make up the color index for 122 | the rightmost pixel. 123 | 124 | ``` 125 | PixelL = (PixelByte & 0xF); 126 | PixelR = (PixelByte >> 4); 127 | 128 | PixelColorL = CLUT[PixelL]; 129 | PixelColorR = CLUT[PixelR]; 130 | ``` 131 | 132 | ### 8bpp 133 | 134 | Each byte represents one pixel. The byte value is used directly in the CLUT to 135 | determine the output color. 136 | 137 | ``` 138 | PixelColor = CLUT[PixelByte]; 139 | ``` 140 | 141 | ### 16bpp 142 | 143 | Two bytes represent one pixel. The color value is derived directly from the 144 | 16-bit value. You can convert the color using the formula mentioned above in 145 | the CLUT section. 146 | 147 | ### 24bpp 148 | 149 | Three bytes represent one pixel. The color value is derived directly from the 150 | 24-bit value. Red intensity comes from the first byte, green comes from the 151 | second, and blue comes from the third. Each intensity value is in a range of 152 | 0 to 255, as opposed to the range of intensities used in the rest of the 153 | format. 154 | 155 | ## Konami specific uses 156 | 157 | Often, Konami will combine a number of TIM files back-to-back in the arcade 158 | version of Dance Dance Revolution. When the end of pixel data is encountered, 159 | check to see if the end of the file has also been reached. If not, and the 160 | next four bytes are the TIM header bytes (10 00 00 00) then try reading another 161 | TIM image. 162 | -------------------------------------------------------------------------------- /iidx/1.md: -------------------------------------------------------------------------------- 1 | # beatmaniaIIDX .1 format 2 | 3 | This document serves to explain the .1 format used by Konami's Beatmania 4 | IIDX arcade series of games. 5 | 6 | ## About this format 7 | 8 | Beatmania charts have been stored in a few different formats over the lifetime 9 | of the franchise. The .1 format is the latest format in use today. It was first 10 | discovered in 9th Style AC. 11 | 12 | ## In a nutshell 13 | 14 | The .1 file format is actually a kind of 'chart archive'. The file is two parts: 15 | the first part is the directory and the second part is where all the actual data is 16 | stored. The directory tells us which difficulties are available and where they 17 | are in the file. 18 | 19 | ## Directory 20 | 21 | There are 12 directory entries. Each entry is 8 bytes long: 22 | 23 | ``` 24 | Offset(h) Type Length Descrption 25 | +00 int32 4 offset of the chart from the beginning of the file 26 | +04 int32 4 length of the chart, in bytes 27 | ``` 28 | 29 | The charts are stored immediately after the directory typically. Although 30 | it is quite possible at a later date Konami will add more charts to the directory, 31 | so don't assume this. 32 | 33 | ## Chart 34 | 35 | Charts are stored as a series of 8-byte events. Each event has an offset, a type, 36 | and some parameters. The end of the chart is determined by the "end of chart" 37 | offset value of 0x7FFFFFFF. This means each chart needs to end with the byte sequence 38 | `FF FF FF 7F 00 00 00 00`. Values are little-endian. 39 | 40 | ``` 41 | Offset(h) Type Length Descrption 42 | +00 int32 4 offset of the event, in ticks 43 | +04 byte 1 type of event 44 | +05 byte 1 event-specific parameter 45 | +06 int16 2 event-specific value 46 | ``` 47 | 48 | ### Tick Timing 49 | 50 | In the arcade versions of Beatmania IIDX, there is no "tick rate" defined in 51 | the chart file itself. You have to infer it from the version of the game: 52 | 53 | ``` 54 | Version Ticks Per Second 55 | Before GOLD 59.94 56 | GOLD 60.046 57 | After GOLD 1000 58 | ``` 59 | 60 | You'll notice precision of the notes greatly increases on TROOPERS and later. 61 | All you have to do is `EventTickOffset / TicksPerSecond` and you will have 62 | the offset of the event in seconds. 63 | 64 | ### Event Types 65 | 66 | ``` 67 | Value(h) Description 68 | 00 Visible note on the playfield for player 1 69 | 01 Visible note on the playfield for player 2 70 | 02 Sample change for player 1 71 | 03 Sample change for player 2 72 | 04 Tempo change 73 | 05 Meter information 74 | 06 End of song 75 | 07 BGM sound 76 | 08 Timing window information 77 | 0C Measure bar 78 | 10 Note count information 79 | ``` 80 | 81 | #### Visible Note (00, 01) 82 | 83 | - Type: 00 (Player 1), 01 (Player 2) 84 | - Parameter: Column number 85 | - Value: If nonzero, defines the length of a freeze note in ticks 86 | 87 | If the value is zero, it's just a regular note. 88 | 89 | #### Sample Change (02, 03) 90 | 91 | - Type: 02 (Player 1), 03 (Player 2) 92 | - Parameter: Column number 93 | - Value: Number of the sample to set the column to 94 | 95 | This is what determines which sound will play when a key is pressed, 96 | but does not actually make a sound by itself. 97 | 98 | #### Tempo Change (04) 99 | 100 | - Type: 04 101 | - Parameter: Denominator 102 | - Value: Numerator 103 | 104 | The BPM is a fraction determined by Numerator/Denominator. Songs can 105 | be found that have either 1 or 100 for the denominator, depending 106 | if a fractional BPM is used. A Parameter of 00 is NOT valid. 107 | 108 | #### Meter Information (05) 109 | 110 | - Type: 05 111 | - Parameter: Denominator 112 | - Value: Numerator 113 | 114 | This tells the game how to count beats. Most songs are 4/4 meter, but 115 | songs such as Holic and Abstract would be in 7/8 part of the time. 116 | If I had to guess, it's used for the flashing graphic in the background 117 | of the play field for synchronizing with beats. 118 | 119 | #### End of Song (06) 120 | 121 | - Type: 06 122 | 123 | This just marks the end of the song. I think when this event is 124 | encountered, it signals to the game engine that it should fade 125 | out and go to grading. 126 | 127 | #### BGM Sound (07) 128 | 129 | - Type: 07 130 | - Parameter: Stereo panning (01-0F, left to right, 08 is center) 131 | - Value: Sound number to play 132 | 133 | This event just plays a sound in the background and is not visible 134 | on the field. 135 | 136 | #### Timing Window Information 137 | 138 | - Type: 08 139 | - Parameter: Which part of the window to define 140 | - Value: Frame count 141 | 142 | The frame count is an 8-bit signed value and is counted in the number 143 | of frames *ahead* of the note a player presses. JUST GREAT is implicitly 144 | a window of 0 and 1. That means values below 0 are late, and values above 145 | 0 are early. 146 | 147 | A lot of good information about this type can be found here: 148 | https://zenius-i-vanisher.com/v5.2/viewthread.php?threadid=5233 149 | 150 | Here's the list of parameters: 151 | ``` 152 | Value(h) Description 153 | 00 latest end of the judgement window 154 | 01 latest end of the GOOD window 155 | 02 latest end of the GREAT window 156 | 03 earliest end of the GREAT window 157 | 04 earliest end of the GOOD window 158 | 05 earliest end of the judgement window 159 | ``` 160 | 161 | Here's a visual representation of values -16, -6, -1, 3, 8, 18 courtesy of 162 | Catastrophe from the above link: 163 | 164 | ![Default timing visualization](images/catastrophe-iidxtiming.png) 165 | 166 | In some double charts, these values are included twice. It is not known at 167 | this time whether this has an effect on gameplay. 168 | 169 | Bonus GAMBOL trivia: the timing window is -15, -7, 0, 2, 9, 17. 170 | 171 | #### Measure Bar 172 | 173 | - Type: 0C 174 | - Parameter: Which player it is visible for 175 | 176 | This is just a visual element, rendered as a measure bar across the 177 | play field. 178 | 179 | #### Note Count Information 180 | 181 | - Type: 10 182 | - Parameter: Which player it is a count for 183 | - Value: Number of playable notes 184 | 185 | This just indicates how many playable notes there are in the chart. 186 | 187 | ### Columns 188 | 189 | In IIDX, this is the column mapping: 190 | 191 | ``` 192 | Value(h) Description 193 | 00 Key 1 194 | 01 Key 2 195 | 02 Key 3 196 | 03 Key 4 197 | 04 Key 5 198 | 05 Key 6 199 | 06 Key 7 200 | 07 Scratch 201 | ``` 202 | -------------------------------------------------------------------------------- /iidx/images/catastrophe-iidxtiming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SaxxonPike/rhythm-game-formats/6f4310dba70d468f45f6d5217907bb632c978146/iidx/images/catastrophe-iidxtiming.png -------------------------------------------------------------------------------- /ios/gen.md: -------------------------------------------------------------------------------- 1 | Format used by [Dance Dance Revolution S+](https://itunes.apple.com/us/app/dancedancerevolution-s+-us/id300655935?mt=8) on iOS. 2 | 3 | Not much is known about this format internally. It appears to contain graphics (PVR format), an MP3 preview, an MP3 of the full song, and the song information all in one file (stored at the end). 4 | 5 | When downloaded from the store, a hashing algorithm is used likely for file verification purposes (checksum) and then the first 8 bytes are of the file are stripped. The file is then a valid `.gen` file. 6 | 7 | # Header 8 | 9 | The header consists of absolute file offsets and sizes. Each value is a 32-bit integer (4 bytes) in little-endian format. 10 | 11 | The game, probably for performance reasons, only decrypts what is absolutely necessary at the time. For example, the game will not decrypt any part of the MP3 contained until you need to hear it, such as when you start playing the song. 12 | 13 | The header starts at `0x00` and ends `0x3F`, leaving room for 7 file offsets and sizes. 14 | 15 | Almost all sections are encrypted using a Blowfish implementation. They all start with the characters `b"KDEI"`. Each format described is *after* the decryption process. 16 | 17 | Since all sections in encrypted form start with `b"KDEI"`, it is likely these 4 bytes must be removed prior to beginning decryption. 18 | 19 | Offsets and descriptions of content at said offset: 20 | 21 | * `0x00`: song (MP3), MP3 format 22 | * `0x08`: preview song, MP3 format 23 | * `0x10`: song banner in PVR format. This may not be a very high quality image and is optimised for use with the game engine. As a result, it appears the texture is stored upside-down and mirrored horizontally. To get the correct image, a 180 degree rotation and mirror is required. 24 | * `0x18`: steps in [SSQ format](/ddr/ssq.md), with a chunk type 0x09 at the end storing metadata 25 | * `0x20`: unknown 26 | * `0x28`: song information including title and artist. There are usually 3 fields that can hold a maximum of 70 characters. The first 3 bytes of each field is the length of the string (even though the fields are zero-padded, this is not what determines the end and these are not C strings). The string length is in big-endian (example: `00 00 0e` for 15). This field is not encrypted. All strings are encoded in UTF-8 27 | * `0x30`: unknown; size is always small (0x24 (36) bytes as an example) 28 | * `0x38`: unknown; same idea as what is at `0x30` 29 | 30 | ```c 31 | const unsigned int SECTION_SIZE = 8; 32 | uint32_t abs_file_offset, expected_size; 33 | 34 | fseek(file_handle, SECTION_SIZE * section_number, 0); 35 | 36 | fread(&abs_file_offset, 4, 1, file_handle); 37 | fread(&expected_size, 4, 1, file_handle); 38 | ``` 39 | 40 | ```c 41 | struct gen_hdr { 42 | uint32_t song_offset; 43 | uint32_t song_size; 44 | 45 | uint32_t song_preview_offset; 46 | uint32_t song_preview_size; 47 | 48 | uint32_t unk_texture1_offset; 49 | uint32_t unk_texture1_size; 50 | 51 | uint32_t ssq_offset; 52 | uint32_t ssq_size; 53 | 54 | uint32_t unk1_offset; 55 | uint32_t unk1_size; 56 | 57 | uint32_t song_info_offset; 58 | uint32_t song_info_size; 59 | 60 | uint32_t unk2_offset; 61 | uint32_t unk2_size; 62 | 63 | uint32_t unk3_offset; 64 | uint32_t unk3_size; 65 | }; 66 | ``` 67 | 68 | # Reading song information 69 | 70 | If the length read does not make sense (exceeds 70) with the 3 bytes, it should be ignored and the data should be read until `\0` with a limit of 70. 71 | 72 | The length value is still the same, even if the data is in Japanese (or a multi-byte sequence of any type). The length in the case of *TЁЯRA* is still 5, although the actual length of the data in UTF-8 encoding is 13. So for the purpose of data extraction, it may be better to assume to read until `\0` in every case, with a maximum length of 70. 73 | 74 | ```c 75 | const unsigned int SECTION_SIZE = 8; 76 | const unsigned int SONG_SECTION_NUMBER = 5; 77 | struct gen_hdr gen_info; 78 | char *data, artist[70], title[70], alt_title[70]; 79 | unsigned int length; 80 | 81 | fseek(file_handle, SONG_SECTION_NUMBER * SECTION_SIZE, 0); 82 | fread(&gen_info.song_info_offset, 4, 1, file_handle); 83 | fread(&gen_info.song_info_size, 4, 1, file_handle); 84 | 85 | fseek(file_handle, gen_info.song_info_offset, 0); 86 | data = malloc(gen_info.song_info_size); 87 | fread(data, gen_info.song_info_size, 1, file_handle); 88 | 89 | // Get title 90 | // data + 73 for 'alternate' title (may be a romaji title) 91 | // data + 143 for artist 92 | memcpy(&length, data, 3); 93 | endian_swap_if_necessary(&length); 94 | memcpy(&title, data + 3, length); 95 | 96 | free(data); 97 | ``` 98 | 99 | # Banner 100 | 101 | The PVR format used in this game is readable with [QuickPVR](https://github.com/Volcore/quickpvr). The exact PVR format used is not yet known (there are [many choices](https://github.com/Volcore/quickpvr/blob/master/pvr.cc#L70)). 102 | -------------------------------------------------------------------------------- /ios/jbt-rb-orb.md: -------------------------------------------------------------------------------- 1 | # Zip files with special extensions 2 | 3 | `.jbt`, `.orb`, and `.rb` are formats used generally by the iOS versions of BEMANI games *jubeat plus*, *jukebeat*, *Reflec Beat Plus*, *Rb+*, and *pop'n rhythmin*. The `.jbt` format is also used with the Android version of *jubeat plus*. 4 | 5 | All 3 formats are zip files with a non-standard extension. You can use any program capable of unzipping zip files to unzip them. There is nothing special about the zip file itself. 6 | 7 | *pop'n rhythm* also has `.acv` files that are for arcade play data. 8 | 9 | ## Files inside 10 | 11 | All files inside are encrypted using Blowfish (in all games including Android (C++) the class name is `BFCodec`) with a custom set of P and S boxes. The IV seems to be static in all cases. Known keys or IVs will not be disclosed here. 12 | 13 | There are at least 2 keys in existence which are calculated like so, from an obfuscated string stored in the binary (which contains both keys at different offsets: 0 and 8 characters in): 14 | 15 | ```python 16 | from hashlib import md5 17 | 18 | unobfuscated = [] 19 | 20 | # where obfuscated_str is a bytearray instance 21 | for i in range(0, 26): # For alternate key, range(0, 22) and in the loop add 7 to each result 22 | unobfuscated.append(obfuscated_str[i] + i) 23 | 24 | # unobfuscated is a list of integers but it is human-readable: map(chr, unobfuscated) 25 | 26 | key = md5(unobfuscated).digest() # 16 integers (the MD5 bytes) which are used as the key (passed as const char *) 27 | ``` 28 | 29 | Which key to use may be determined in the encrypted file itself. 30 | 31 | # While encrypted 32 | 33 | All files inside the zip file contain 8 bytes at the end that are used for verification purposes prior to decryption. These appear to be offsets. The last 4 bytes contain the start of the verification bytes. 34 | 35 | The decryption algorithm will remove the last 8 bytes off the read file data before decryption. 36 | 37 | # File formats and purpose 38 | 39 | *Note: These are only the formats described once decrypted.* 40 | 41 | For note format files, it is generally assumed that the notes themselves are encoded the same way as their arcade counterparts, except in separate files. There is no arcade format to compare to for *pop'n rhythmin'*. 42 | 43 | ## artwork 44 | 45 | Artwork image. This is a PNG inside. It is Apple's version so [`pngdefry`](http://www.jongware.com/pngdefry.html) is necessary. Assume so for all PNGs. 46 | 47 | ## bgm 48 | 49 | An M4A (AAC) file of the song. 50 | 51 | ## info 52 | 53 | [Property list](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html) in XML. Contains song information such as artist, title, etc. 54 | 55 | # Jubeat-specific 56 | 57 | ## artwork_s 58 | 59 | Artwork image (purpose unknown). This may be a retina version of `artwork`. 60 | 61 | ## index 62 | 63 | Unknown. The name sounds like it might be used for sorting purposes. 64 | 65 | ## name_b 66 | 67 | Unknown. 68 | 69 | ## name_w 70 | 71 | Unknown. 72 | 73 | ## seq_adv 74 | 75 | Advanced level notes. 76 | 77 | ## seq_bas 78 | 79 | Basic level notes. 80 | 81 | ## seq_ext 82 | 83 | Extreme level notes. 84 | 85 | # Reflec Beat-specific 86 | 87 | ## artist_b 88 | 89 | Unknown. Assumed to be an image. 90 | 91 | ## artist_b2x 92 | 93 | Unknown purpose. Assumed to be *@2x* (retina) version of `artist_b`. 94 | 95 | ## artist_w 96 | 97 | Same as `artist_b`. 98 | 99 | ## artist_w2x 100 | 101 | Same as `artist_w`. 102 | 103 | ## artwork_2x 104 | 105 | Retina version of `artwork`. 106 | 107 | ## note_bas 108 | 109 | Basic level notes. 110 | 111 | ## note_har 112 | 113 | Hard level notes. 114 | 115 | ## note_med 116 | 117 | Medium level notes. 118 | 119 | ## pre 120 | 121 | Preview in M4A (AAC) format. 122 | 123 | ## title_b 124 | 125 | Title card image in Apple's PNG format. 126 | 127 | ## title_b2x 128 | 129 | *@2x* (retina) version of `title_b`. 130 | 131 | ## title_w 132 | 133 | Image. Unknown purpose. 134 | 135 | ## title_w2x 136 | 137 | *@2x* (retina) version of `title_w`. 138 | 139 | # Popn'n Rhythmin-specific 140 | 141 | ## sheet_n 142 | 143 | Normal level notes. 144 | 145 | ## sheet_h 146 | 147 | Hard level notes. 148 | 149 | ## sheet_ex 150 | 151 | Extreme level notes. 152 | 153 | ## title_2x 154 | 155 | Title card. Rhythmin' is a new game and does not support pre-iOS 7 as of version 2.0.0. As a result all images are retina optimised and no regular size images are provided. 156 | 157 | ## artist_2x 158 | 159 | Artist image? Unknown purpose. 160 | --------------------------------------------------------------------------------