mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-18 23:02:56 -05:00
Added typst version of spec in place of ODT
This commit is contained in:
parent
4e150eac32
commit
9be4a56823
3 changed files with 178 additions and 28 deletions
2
documents/DOCUMENTS.md
Normal file
2
documents/DOCUMENTS.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Typst Documents
|
||||||
|
To compile typst documents (with the `.typ` extension), use the [Typst compiler](https://github.com/typst/typst).
|
Binary file not shown.
|
@ -1,39 +1,65 @@
|
||||||
|
#let ver = version(0, 4, 0)
|
||||||
#set document(
|
#set document(
|
||||||
title: "The CZ# Image Formats",
|
title: "The CZx Image Formats - " + str(ver),
|
||||||
author: "G2",
|
author: "G2",
|
||||||
date: auto,
|
|
||||||
)
|
)
|
||||||
#set text(font: "Roboto", lang: "en", size: 9.3pt)
|
#set text(
|
||||||
#show link: underline
|
font: "Roboto",
|
||||||
#show link: set text(blue)
|
lang: "en",
|
||||||
|
size: 9pt,
|
||||||
|
)
|
||||||
#set page(
|
#set page(
|
||||||
numbering: "1",
|
numbering: "1",
|
||||||
margin: 0.5in,
|
margin: 1.5cm,
|
||||||
paper: "ansi-a",
|
paper: "a4",
|
||||||
)
|
)
|
||||||
|
#set par(leading: 0.7em)
|
||||||
|
#set block(spacing: 1.7em)
|
||||||
|
|
||||||
#text(size: 2.2em, weight: "bold")[The CZx Image Formats]
|
// Styling
|
||||||
#v(1em, weak: true)
|
#show link: underline
|
||||||
#text(size: 1.1em)[Specification #strong[Version 0.3] — Sept. , 2024]
|
#show link: set text(blue)
|
||||||
|
|
||||||
|
#text(size: 22pt, weight: "bold", font: "Roboto Slab")[The CZx Image Formats]
|
||||||
|
#v(1.5em, weak: true)
|
||||||
|
#text(size: 1.1em)[Specification #strong[Version #ver] — Sept. 11, 2024]
|
||||||
|
|
||||||
#line(length: 100%, stroke: 1.5pt + gray)
|
#line(length: 100%, stroke: 1.5pt + gray)
|
||||||
|
|
||||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
|
||||||
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
|
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
|
||||||
interpreted as described in IETF #link("https://datatracker.ietf.org/doc/html/rfc2119")[RFC2119].
|
interpreted as described in IETF
|
||||||
|
#link("https://datatracker.ietf.org/doc/html/rfc2119")[RFC2119].
|
||||||
|
|
||||||
The CZx family of image formats (CZ0, CZ1, CZ2, CZ3, CZ4, and CZ5) are used in
|
The CZx family of image formats (CZ0, CZ1, CZ2, CZ3, CZ4, and CZ5) are used in
|
||||||
the LUCA System visual novel engine developed by Prototype Ltd. These image
|
the LUCA System visual novel engine developed by
|
||||||
formats can be used for storing lossless compressed and uncompressed pixel data
|
#link("https://www.prot.co.jp/")[Prototype Ltd]\. These image formats can be
|
||||||
over a wide range of bit depths and with accompanying metadata useful for a
|
used for storing lossless compressed and uncompressed pixel data over a wide
|
||||||
visual novel. All bytes in CZx files MUST be stored in little-endian format.
|
range of bit depths and with accompanying metadata useful for a visual novel.
|
||||||
|
All bytes in CZx files MUST be stored in little-endian format.
|
||||||
|
|
||||||
|
#show heading: set text(1.2em)
|
||||||
|
#show heading.where(level: 1): head => [
|
||||||
|
#set text(18pt, font: "Roboto Slab", weight: "bold")
|
||||||
|
#head
|
||||||
|
#v(0.3em)
|
||||||
|
]
|
||||||
|
#show heading.where(level: 2): set text(weight: 600)
|
||||||
|
#show raw: it => [
|
||||||
|
#box(stroke: 1pt, width: 100%, inset: 5pt, radius: 3pt)[
|
||||||
|
#it
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
= Header
|
= Header
|
||||||
#columns(2)[
|
|
||||||
== Block 1 — Basic Header
|
|
||||||
All CZx files MUST begin with this header block. The header contains information about basic parameters of the image, such as the bitmap dimensions. The common part of the header is as follows:
|
|
||||||
|
|
||||||
#box(stroke: 1pt, width: 100%, inset: 5pt, radius: 3pt)[
|
#columns(2)[
|
||||||
|
|
||||||
|
== Block 1 — Basic Header
|
||||||
|
All CZx files MUST begin with this header block. The header contains information
|
||||||
|
about basic parameters of the image, such as the bitmap dimensions. The common
|
||||||
|
part of the header is as follows:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
CommonHeader {
|
CommonHeader {
|
||||||
magic: [char; 4], // magic bytes, ex. “CZ0\0”
|
magic: [char; 4], // magic bytes, ex. “CZ0\0”
|
||||||
|
@ -45,11 +71,12 @@ CommonHeader {
|
||||||
unknown: u8, // unknown purpose, often 3
|
unknown: u8, // unknown purpose, often 3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
This common header MAY be followed by an extended header, which contains metadata such as cropping parameters and positioning on the screen. This part of the header exists if the header_length value is greater than 15 bytes, with the exception of the CZ2 format. An example of the extended header is as follows:
|
This common header MAY be followed by an extended header, which contains
|
||||||
|
metadata such as cropping parameters and positioning on the screen. This part of
|
||||||
|
the header exists if the header_length value is greater than 15 bytes, with the
|
||||||
|
exception of the CZ2 format. An example of the extended header is as follows:
|
||||||
|
|
||||||
#box(stroke: 1pt, width: 100%, inset: 5pt, radius: 3pt)[
|
|
||||||
```rust
|
```rust
|
||||||
ExtendedHeader {
|
ExtendedHeader {
|
||||||
unknown: [u8; 5], // Set to 0
|
unknown: [u8; 5], // Set to 0
|
||||||
|
@ -61,18 +88,32 @@ ExtendedHeader {
|
||||||
bounds_height: u16, // height of crop bounds
|
bounds_height: u16, // height of crop bounds
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
The extended header MAY be followed by image offset information, used for positioning the image. This information only exists if the header_length value is greater than 28 bytes. An example of the offset header is as follows:
|
The extended header MAY be followed by image offset information, used for
|
||||||
|
positioning the image. This information only exists if the header_length value
|
||||||
|
is greater than 28 bytes. An example of the offset header is as follows:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
OffsetHeader {
|
||||||
|
offset_width: u16,
|
||||||
|
offset_height: u16,
|
||||||
|
|
||||||
|
unknown: [u8; 4],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
== Block 2 — Indexed Color Information
|
== Block 2 — Indexed Color Information
|
||||||
If the depth of the image is 8 bits per pixel, the header MUST be followed by a palette block containing the information required to properly decode an image which is encoded in indexed color.
|
If the depth of the image is 8 bits per pixel, the header MUST be followed by a
|
||||||
The palette is an ordered list of colors. The color in the first position MUST correspond to an index value of 0 in the image, the second corresponding to a value of 1, and so on. These colors are stored in 8 bit RGBA format.
|
palette block containing the information required to properly decode an image
|
||||||
|
which is encoded in indexed color.
|
||||||
|
|
||||||
The length of the palette corresponds to the bit depth of the image. Therefore, the color list MUST be 256 colors long.
|
The palette is an ordered list of colors. The color in the first position MUST
|
||||||
|
correspond to an index value of 0 in the image, the second corresponding to a
|
||||||
|
value of 1, and so on. These colors are stored in 8 bit RGBA format.
|
||||||
|
|
||||||
|
The length of the palette corresponds to the bit depth of the image. Therefore,
|
||||||
|
the color list MUST be 256 colors long.
|
||||||
|
|
||||||
#box(stroke: 1pt, width: 100%, inset: 5pt, radius: 3pt)[
|
|
||||||
```rust
|
```rust
|
||||||
Color {
|
Color {
|
||||||
red: u8,
|
red: u8,
|
||||||
|
@ -85,6 +126,113 @@ ColorPalette {
|
||||||
colors: [Color; 256]
|
colors: [Color; 256]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
== Block 3 — Compression Information <compression-info>
|
||||||
|
All CZx formats except for CZ0 MUST have a block immediately following the color
|
||||||
|
information which contains information about the size of chunks in the following
|
||||||
|
compressed image data. The compression block starts with the number of
|
||||||
|
compressed blocks, followed by a list of the sizes of the compressed data and
|
||||||
|
original data.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ChunkInfo {
|
||||||
|
compressed_size: u32, // compressed size, bytes
|
||||||
|
original_size: u32, // original size, bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressionInfo {
|
||||||
|
chunk_number: u32, // the number of chunks
|
||||||
|
chunks: ChunkInfo,
|
||||||
|
}
|
||||||
|
```
|
||||||
]
|
]
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
= File Types
|
||||||
|
|
||||||
|
#columns(2)[
|
||||||
|
|
||||||
|
== CZ0
|
||||||
|
CZ0 files are uncompressed, storing raw RGBA pixel data in a linear bitmap.
|
||||||
|
|
||||||
|
This format is most often used to store character sprites, UI elements, and
|
||||||
|
various other game assets. Use of CZ0 has decreased in more recent LUCA System
|
||||||
|
games.
|
||||||
|
|
||||||
|
The encoding used in these files is a simple bitmap of RGBA pixels. Decoding CZ0
|
||||||
|
is as simple as reading the header to determine the width and height of the
|
||||||
|
image in pixels, and then reading the image data as 4 byte RGBA chunks, which
|
||||||
|
correspond directly to pixels.
|
||||||
|
|
||||||
|
== CZ1
|
||||||
|
CZ1 files are compressed, storing raw RGBA pixel data using LZW compression.
|
||||||
|
|
||||||
|
This format is used to store text bitmaps in older LUCA System games, along with
|
||||||
|
UI elements and other small image assets in more recent games. It is most often
|
||||||
|
encountered with 8 bit indexed color, but 32 bit RGBA is also relatively common.
|
||||||
|
|
||||||
|
== CZ2
|
||||||
|
CZ2 files are compressed, storing raw RGBA pixel data using LZW compression.
|
||||||
|
This method of compression is different from CZ1.
|
||||||
|
|
||||||
|
This format is primarily used for storing text bitmaps in newer LUCA System
|
||||||
|
games. Its use outside of text bitmaps is limited.
|
||||||
|
|
||||||
|
#colbreak()
|
||||||
|
|
||||||
|
== CZ3
|
||||||
|
CZ3 files are compressed, storing modified RGBA pixel data using LZW
|
||||||
|
compression. This compression scheme is the same as CZ1.
|
||||||
|
|
||||||
|
This format is primarily used for storing backgrounds, but is also used for
|
||||||
|
sprites, character graphics, and general files. It appears to be the most
|
||||||
|
general form of more highly compressed CZx files. The compression ratios
|
||||||
|
achieved by CZ3 are similar to or slightly worse than a
|
||||||
|
PNG file with a compression level of 5.
|
||||||
|
|
||||||
|
== CZ4
|
||||||
|
CZ4 files are compressed, storing modified RGBA pixel data using LZW
|
||||||
|
compression. This compression scheme is the same as CZ1.
|
||||||
|
|
||||||
|
This format only appears in newer LUCA System games, and is primarily used for
|
||||||
|
storing sprites, character graphics, and backgrounds. It seems to have replaced
|
||||||
|
the use of CZ3 files and CZ0 files in many places in the engine, but not
|
||||||
|
entirely. The compression ratios achieved by CZ4 are similar to or slightly
|
||||||
|
better than a PNG file with a compression level of 9.
|
||||||
|
|
||||||
|
== CZ5
|
||||||
|
Little is known about the CZ5 format, as it has not been encountered in any
|
||||||
|
released games so far. The only information about it has come from decompiling
|
||||||
|
recent games which use the LUCA System engine, where it is referenced as part of
|
||||||
|
the decoder for CZx files.
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
#v(2em)
|
||||||
|
#line(length: 100%, stroke: 1.5pt + gray)
|
||||||
|
|
||||||
|
= Compression Methods
|
||||||
|
The two types of compression used in CZx files are Type 1 (used in CZ1, CZ3, and
|
||||||
|
CZ4 files) and Type 2 (used in CZ2 files). On top of these two types, CZ3 and
|
||||||
|
CZ4 have extra modifications to the image data to make it possible to compress
|
||||||
|
them further. Both of these methods are dictionary based compression algorithms.
|
||||||
|
|
||||||
|
== Type 1 (CZ1-style)
|
||||||
|
Type 1 compression is a dictionary based compression algorithm that has a
|
||||||
|
fixed-length block size. The data MUST be read and written in blocks which are
|
||||||
|
sized according to the compressed_size value in the compression information
|
||||||
|
section of the header. When creating compressed data, the block size (which
|
||||||
|
determines the compressed_size value) SHOULD be set to 0xFEFD, however, it MAY
|
||||||
|
be set to a smaller value, and MUST NOT be set to a larger value, as this will
|
||||||
|
break compatibility with existing decoders.
|
||||||
|
|
||||||
|
To decode Type 1 compression,
|
||||||
|
|
||||||
|
== Type 2 (CZ2-style)
|
||||||
|
Type 2 compression is a dictionary based compression algorithm that has a
|
||||||
|
variable-length block size. The data MUST be read in blocks which are sized
|
||||||
|
according to the compressed_size value in the
|
||||||
|
#link(<compression-info>)[compression information] section of the header. When
|
||||||
|
creating compressed data, the block size is dynamic based on the number of
|
||||||
|
entries that can fit in the 18-bit dictionary.
|
||||||
|
|
Loading…
Reference in a new issue