Fonts.cv

darkpanda

Dark Prince
Joined
Oct 28, 2007
Messages
844
Yet another thread hacking into CivDOS resource files! This time it's FONTS.CV, the resource files containing CivDOS fonts.

So, FONTS.CV contains multiple Fonts used by Civ: opening credits fonts, newspaper header fonts, in-game menu fonts, different fonts in Diplomacy screens depending on the Civ (only in EN version), etc...

The FONTS.CV data is structure as follows:
Code:
[B]
[FONT="Courier New"]FONTS.CV := 
   <font_count>
   <font_offset_0>
     ...
   <font_offset_[I]N[/I]>
   <fontset_0>
     ...
   <fontset_[I]N[/I]>[/FONT][/B]

  • <font_count> is a 2-byte little-endian (unsigned?) short integer, encoding the number of fonts contained in the file; for the EN 475.01 version, it contains 9, for the FR version it contains 7
  • Then the file contains as many <font_offset_i> as the value of <font_count> specified above. Each of those is also a 2-byte little-endian unsigned short integer, encoding an offset into the file where the actual font face data begins
  • After that, the file contains as many <fontset_i> data blocks as the value of <font_count specified at the beginning of the file. Those data blocks sizes depend on the font face, but their structure is fixed, as explained below:

Code:
[B][FONT="Courier New"]
<fontset_[I]i[/I]> := 
   <[B]char_widths[/B]>
   <font_ascii_first>
   <font_ascii_last>
   <char_byte_length>
   <char_top_row>
   <char_bottom_row>
   <[B]font_spacings[/B]>
   <unknown_byte>
   <font_face_data>[/FONT]
[/B]

  • <char_widths> is an array of bytes, representing the pixel width of each character in the font:
    • it contains 1 byte per supported character, whose value is the width, in pixels, of the font face for this character
    • the length of this array is the total count of characters supported by the font (see below)
  • <font_ascii_first> is a single unsigned byte representing the [wiki]ASCII[/wiki] char code of the first character represented by the fontset; for the EN version, it is 32 (0x20, blank space) for all fonts; for the FR version, many fonts start from [wiki]ASCII[/wiki] code 0x0
  • <font_ascii_last> is a single unsigned byte representing the [wiki]ASCII[/wiki] char code of the last character represented by the fontset; for the EN version, it varies from 122 (0x7B) to 127 (0x7F)
  • <bytes_per_char> is a single byte representing the number of bytes used to encode 1 row of pixels for each character in the <font_face_data> block (see below); among all the available fonts in either EN or FR versions, its value is only ever 1 or 2
  • <char_top_row> is a single byte representing the ID of the first row of pixels (top) encoded in the font face data; it's always 0, for all fonts
  • <char_bottom_row> is a single byte representing the ID of the last row of pixels (bottom) encoded in the font face data; it varies from 5 to 16 (0x10) from font to font
  • <font_spacings> is a set of 2 bytes that code respectively for the horizontal spacing (between chars) and vertical spacing (between lines); for all fonts in all versions encountered, their values is always "0x0101"
  • <unknown_byte> is a single byte, whose value is always 0x00; its usage is unknown yet
  • <font_face_data> is an array of bytes representing the font face pixel data; its structure is explained below.

Notes:
  • the charset_size is computed as the count of supported characters in the font's ASCII range; that is <font_ascii_last> - font_ascii_first> + 1:
    • e.g. if the first and last supported ASCII chars are 32 (0x20) and 127 (0x7F), the charset size is 127-32+1 = 96 characters;
  • strangely enough, the value of <font_offset_i> at the beginning of the file is not the offset of the first font's data block (<char_widths>), but rather the offset of <font_face_data>; this makes it typically impossible to parse this file in a streaming fashion, since one needs to access the offset first, then read backwards to get the font size, range, spacings, widths and height, then proceed forward with the font face data
  • a more intuitive way of understanding <char_top_row> and <char_bottom_row> is to read them as the font face height in pixels:
    • e.g. if top row is 0 and bottom row is 11 (0xB), that means there are totally 11 - 0 + 1 = 12 rows of pixels, i.e. the font's height is 12 pixels.


Now for the real juice, here is the data structure of <font_face_data>, the font's characters' pixel data:

Code:
[B]<font_face_data> :=
   <pixel_row_0>
   ...
   <pixel_row_[I]N[/I]>
[/B]

   where:
[B]   <pixel_row_i> :=
      <char0_row_i_bytes>
      <char1_row_i_bytes>
        ...
      <charM_row_i_bytes>
[/B]

      where:
[B]      <charX_row_i_bytes> :=
         <charX_row_i_byte_0>
           ...
         <charX_row_i_byte_p>[/B]

A more wordy description of the structure above:
  • the pixel data is stored as rows of pixels spanning across the entire charset:
    • to understand this, imagine the whole charset printed from left to right, on a single line; then, encode the corresponding pixels from left to right, starting in the top-left corner;
    • that is to say, the first array of bytes contains the first row of, pixels for all of the font's characters
    • then the next array of bytes contains the 2nd row of pixels for all of the font's characters, and so on...
    • given <char_top_row> and <char_bottom_row>, we deduct that <font_face_data> contains (<char_bottom>row> - <char_top_row> + 1) elements <pixel_row_i>
  • each <pixel_row_i> is an array of bytes, containing <bytes_per_char> bytes per supported character, coding for this character's pixels in this row; that is to say each pixel row contains <bytes_per_char> * <charset_size> bytes
  • if there is more than 1 byte (meaning 2 bytes), they are loaded in big-endian order, i.e. high-order byte comes first;
  • each single <charX_row_i_byte_0> byte represents pixel data starting with the high-order bit first (strangely enough...):
    • if the bit value is 0, then the pixel is transparent
    • if the bit value is 1, then the pixel is opaque

Now, if this is a little arduous to get through, here is a little hands-on, looking at CivDOS EN 475.01 version of FONTS.CV:

The first 2 bytes of FONTS.CV are:
Code:
[0x0000]  [color=blue]09 00[/color] ...

This means that <font_count> = 9

So we go on to read the next 9 unsigned shorts:

Code:
[0x0000]  09 00 [color=blue]7c 00 e4 03 8c 06 2f 0f ac 1b 90 28 38 34[/color]
[0x0010]  [color=blue]60 3b 47 43[/color] ...

Those are the in-file offsets to the 9 fonts' pixel data contained in FONTS.CV:
Code:
<font_offset_0> = 0x007C
<font_offset_1> = 0x03E4
<font_offset_2> = 0x068C
<font_offset_3> = 0x0F2F
<font_offset_4> = 0x1BAC
<font_offset_5> = 0x2890
<font_offset_6> = 0x3438
<font_offset_7> = 0x3B60
<font_offset_8> = 0x4347

For this hands-on, we look at font with id 4 (i.e. the 5th font in the file), whose offset is 0x1BAC

Around offset 0x1BAC, highlighted in red below, here is what we find:
Code:
[0x1B20] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[0x1B30] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[0x1B40] 00 00 00 00 00 00 05 02 03 06 06 03 05 02 10 10
[0x1B50] 10 04 10 10 0e 00 0a 0a 09 0a 09 08 0a 09 08 09
[0x1B60] 02 02 05 05 05 05 05 09 08 07 08 07 07 07 09 04
[0x1B70] 08 09 07 0d 09 07 09 07 09 07 08 09 09 0d 09 0a
[0x1B80] 08 00 00 00 00 00 00 08 08 06 08 06 07 07 09 04
[0x1B90] 03 08 04 0e 09 07 08 08 08 07 07 09 09 0d 09 09
[0x1BA0] 09 03 03 03 20 7d 02 00 10 01 01 00 [color=RED]00[/color] 00 40 00
[0x1BB0] a0 00 00 00 00 00 00 00 20 00 c0 00 00 00 00 00
[0x1BC0] 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 80
[0x1BD0] 1c 00 17 00 01 00 1f 00 01 80 3f 80 3c 00 1e 00
[0x1BE0] 00 00 00 00 00 00 00 00 00 00 70 00 00 00 18 00
[0x1BF0] fc 00 3a 00 fc 00 fe 00 fe 00 3a 00 f7 80 f0 00
[0x1C00] 0f 00 f6 00 f0 00 e0 38 c3 80 38 00 7e 00 38 00
[0x1C10] fc 00 3a 00 ff 00 f3 80 f3 80 e3 38 f3 80 f1 c0
[0x1C20] ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[0x1C30] e0 00 00 00 0e 00 00 00 18 00 00 00 e0 00 00 00
[0x1C40] 00 00 e0 00 e0 00 00 00 00 00 00 00 00 00 00 00

First we look at the 8 bytes before 0x1BAC, that contain the font characteristics:
Code:
[0x1BA0] 09 03 03 03 [color=blue]20 7d 02 00 10 01 01 00[/color] [color=RED]00[/color] 00 40 00

As per our formal description above, it gives use the following values:
Code:
<font_ascii_first> = 0x20 (32, blank space)
 <font_ascii_last> = 0x7d (125, closing accolade '}')
  <bytes_per_char> = 0x02 (2 bytes per char)
    <char_top_row> = 0x00
 <char_bottom_row> = 0x10 (16, making the font 17 pixels in height)
   <font_unknowns> = 0x010100 (unknown...)

We deduct that the size of the charset is 125-32+1 = 94 chars... Then we just need to look at the 94 bytes placed before the <font_ascii_first>, that represent the <char_widths>, that is the width in pixels of each char in the font:
Code:
[0x1B20] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[0x1B30] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[0x1B40] 00 00 00 00 00 00 [color=green]05 02 03 06 06 03 05 02 10 10[/color]
[0x1B50] [color=green]10 04 10 10 0e 00 0a 0a 09 0a 09 08 0a 09 08 09[/color]
[0x1B60] [color=green]02 02 05 05 05 05 05 09 08 07 08 07 07 07 09 04[/color]
[0x1B70] [color=green]08 09 07 0d 09 07 09 07 09 07 08 09 09 0d 09 0a[/color]
[0x1B80] [color=green]08 00 00 00 00 00 00 08 08 06 08 06 07 07 09 04[/color]
[0x1B90] [color=green]03 08 04 0e 09 07 08 08 08 07 07 09 09 0d 09 09[/color]
[0x1BA0] [color=green]09 03 03 03[/color] [color=blue]20 7d 02 00 10 01 01 00[/color] [color=RED]00[/color] 00 40 00
[0x1BB0] a0 00 00 00 00 00 00 00 20 00 c0 00 00 00 00 00
[0x1BC0] 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 80
[0x1BD0] 1c 00 17 00 01 00 1f 00 01 80 3f 80 3c 00 1e 00
[0x1BE0] 00 00 00 00 00 00 00 00 00 00 70 00 00 00 18 00
[0x1BF0] fc 00 3a 00 fc 00 fe 00 fe 00 3a 00 f7 80 f0 00
[0x1C00] 0f 00 f6 00 f0 00 e0 38 c3 80 38 00 7e 00 38 00
[0x1C10] fc 00 3a 00 ff 00 f3 80 f3 80 e3 38 f3 80 f1 c0
[0x1C20] ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[0x1C30] e0 00 00 00 0e 00 00 00 18 00 00 00 e0 00 00 00
[0x1C40] 00 00 e0 00 e0 00 00 00 00 00 00 00 00 00 00 00


Now is time to get our hands dirty, and look at the pixel data: for the sake of this hands-on, I will focus on the upper case 'A' char, whose ASCII code is 65 (0x41):

  • As per the description above, I will have to extract 2 bytes (<bytes_per_char> = 2) for each of the 17 rows of pixels (<char_bottom_row> - <char_top_row> + 1 = 16 - 0 + 1 = 17) representing this character.
  • In 2 bytes per character per row, we have totally 16 bits, that is 16 horizontal pixels per row per character... Since there are 17 rows, we are basically retrieving pixel data for a 16x17 picture representing the character.
  • As there are totally 94 characters in this font, and each row of each char uses 2 bytes, then each <pixel_row> has a length of 94*2 = 188 bytes
  • The first character represented by the pixel data is the blank space (char code 32 / 0x20); so the 2 first bytes of each <pixel_row> (bytes at index 0 and 1) are assigned to character with code 32
  • Since we are looking for rows of pixels for character 'A', with ASCII code 65, we will look at the 2 bytes of each <pixel_row> at index 0+(65-32)*2 and 1+(65-32)*2, that is index 66 (0x42) and 67 (0x43)
  • Since the offset of the first <pixel_row> is the same as <face_face_data>, that <font_offset_4> determined above as being 0x1BAC, then the 2 bytes representing the top row of char 'A' are at offsets 0x1BAC+0x42 and 0x1BAC+0x43, that is bytes at 0x1BEE and 0x1BEF shown below:
    Code:
    [0x1B20] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    [0x1B30] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    [0x1B40] 00 00 00 00 00 00 [color=green]05 02 03 06 06 03 05 02 10 10[/color]
    [0x1B50] [color=green]10 04 10 10 0e 00 0a 0a 09 0a 09 08 0a 09 08 09[/color]
    [0x1B60] [color=green]02 02 05 05 05 05 05 09 08 07 08 07 07 07 09 04[/color]
    [0x1B70] [color=green]08 09 07 0d 09 07 09 07 09 07 08 09 09 0d 09 0a[/color]
    [0x1B80] [color=green]08 00 00 00 00 00 00 08 08 06 08 06 07 07 09 04[/color]
    [0x1B90] [color=green]03 08 04 0e 09 07 08 08 08 07 07 09 09 0d 09 09[/color]
    [0x1BA0] [color=green]09 03 03 03[/color] [color=blue]20 7d 02 00 10 01 01 00[/color] [color=RED]00[/color] 00 40 00
    [0x1BB0] a0 00 00 00 00 00 00 00 20 00 c0 00 00 00 00 00
    [0x1BC0] 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 80
    [0x1BD0] 1c 00 17 00 01 00 1f 00 01 80 3f 80 3c 00 1e 00
    [0x1BE0] 00 00 00 00 00 00 00 00 00 00 70 00 00 00 [color=magenta][b]18 00[/b][/color]
    [0x1BF0] fc 00 3a 00 fc 00 fe 00 fe 00 3a 00 f7 80 f0 00
    [0x1C00] 0f 00 f6 00 f0 00 e0 38 c3 80 38 00 7e 00 38 00
    [0x1C10] fc 00 3a 00 ff 00 f3 80 f3 80 e3 38 f3 80 f1 c0
    [0x1C20] ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    [0x1C30] e0 00 00 00 0e 00 00 00 18 00 00 00 e0 00 00 00
    [0x1C40] 00 00 e0 00 e0 00 00 00 00 00 00 00 00 00 00 00
  • We find out that <char65_bytes_row0> = 0x1800
  • Since the total byte length for row of pixels is 188 bytes (0xBC), we look for the next 2 bytes, for row 1, for character A, at offset 0x1BEE + 0xBC = 0x1CAA:
    Code:
    [0x1B20] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    [0x1B30] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    [0x1B40] 00 00 00 00 00 00 [color=green]05 02 03 06 06 03 05 02 10 10[/color]
    [0x1B50] [color=green]10 04 10 10 0e 00 0a 0a 09 0a 09 08 0a 09 08 09[/color]
    [0x1B60] [color=green]02 02 05 05 05 05 05 09 08 07 08 07 07 07 09 04[/color]
    [0x1B70] [color=green]08 09 07 0d 09 07 09 07 09 07 08 09 09 0d 09 0a[/color]
    [0x1B80] [color=green]08 00 00 00 00 00 00 08 08 06 08 06 07 07 09 04[/color]
    [0x1B90] [color=green]03 08 04 0e 09 07 08 08 08 07 07 09 09 0d 09 09[/color]
    [0x1BA0] [color=green]09 03 03 03[/color] [color=blue]20 7d 02 00 10 01 01 00[/color] [color=RED]00[/color] 00 40 00
    [0x1BB0] a0 00 00 00 00 00 00 00 20 00 c0 00 00 00 00 00
    [0x1BC0] 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 80
    [0x1BD0] 1c 00 17 00 01 00 1f 00 01 80 3f 80 3c 00 1e 00
    [0x1BE0] 00 00 00 00 00 00 00 00 00 00 70 00 00 00 [color=magenta][b]18 00[/b][/color]
    [0x1BF0] fc 00 3a 00 fc 00 fe 00 fe 00 3a 00 f7 80 f0 00
    [0x1C00] 0f 00 f6 00 f0 00 e0 38 c3 80 38 00 7e 00 38 00
    [0x1C10] fc 00 3a 00 ff 00 f3 80 f3 80 e3 38 f3 80 f1 c0
    [0x1C20] ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    [0x1C30] e0 00 00 00 0e 00 00 00 18 00 00 00 e0 00 00 00
    [0x1C40] 00 00 e0 00 e0 00 00 00 00 00 00 00 00 00 00 00
    [0x1C50] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    [0x1C60] 00 00 40 00 00 00 00 00 00 00 40 00 a0 00 50 00
    [0x1C70] 70 00 00 00 50 00 c0 00 07 f0 01 f8 00 7f 00 00
    [0x1C80] 00 00 00 00 00 00 00 00 0d 80 01 80 36 00 19 80
    [0x1C90] 03 00 30 00 07 00 3f 80 66 00 33 00 00 00 00 00
    [0x1CA0] 18 00 00 00 c0 00 88 00 00 00 [color=magenta][b]18 00[/b][/color] 66 00 66 00
    [0x1CB0] 66 00 66 00 66 00 66 00 63 00 60 00 06 00 66 00
    [0x1CC0] 60 00 60 30 c1 00 6c 00 33 00 6c 00 66 00 66 00
    [0x1CD0] db 00 61 00 61 00 63 10 63 00 60 80 c3 00 00 00
    [0x1CE0] 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00
  • We find out that <char65_bytes_row1> = 0x1800; it's the same value as row 0, actually
  • Similarly, we will look at the remaining 15 pairs of bytes by making jumps of 188 bytes in the pixel data, and we find the following values:

    <char65_bytes_row0> : offset = 0x1BAC + 0x42 = 0x1BEE -> 0x1800 -> 0b 0001 1000 0000 0000 -> ...##...........
    <char65_bytes_row1> : offset = 0x1BEE + 0xBC = 0x1CAA -> 0x1800 -> 0b 0001 1000 0000 0000 -> ...##...........
    <char65_bytes_row2> : offset = 0x1CAA + 0xBC = 0x1D66 -> 0x1C00 -> 0b 0001 1100 0000 0000 -> ...###..........
    <char65_bytes_row3> : offset = 0x1D66 + 0xBC = 0x1E22 -> 0x2C00 -> 0b 0010 1100 0000 0000 -> ..#.##..........
    <char65_bytes_row4> : offset = 0x1E22 + 0xBC = 0x1EDE -> 0x2C00 -> 0b 0010 1100 0000 0000 -> ..#.##..........
    <char65_bytes_row5> : offset = 0x1EDE + 0xBC = 0x1F9A -> 0x2C00 -> 0b 0010 1100 0000 0000 -> ..#.##..........
    <char65_bytes_row6> : offset = 0x1F9A + 0xBC = 0x2056 -> 0x2600 -> 0b 0010 0110 0000 0000 -> ..#..##.........
    <char65_bytes_row7> : offset = 0x2056 + 0xBC = 0x2112 -> 0x2600 -> 0b 0010 0110 0000 0000 -> ..#..##.........
    <char65_bytes_row8> : offset = 0x2112 + 0xBC = 0x21CE -> 0x4600 -> 0b 0100 0110 0000 0000 -> .#...##.........
    <char65_bytes_row9> : offset = 0x21CE + 0xBC = 0x228A -> 0x4600 -> 0b 0100 0110 0000 0000 -> .#...##.........
    <char65_bytes_row10>: offset = 0x228A + 0xBC = 0x2346 -> 0x7F00 -> 0b 0111 1111 0000 0000 -> .#######........
    <char65_bytes_row11>: offset = 0x2346 + 0xBC = 0x2402 -> 0x4300 -> 0b 0100 0011 0000 0000 -> .#....##........
    <char65_bytes_row12>: offset = 0x2402 + 0xBC = 0x24BE -> 0x4300 -> 0b 0100 0011 0000 0000 -> .#....##........
    <char65_bytes_row13>: offset = 0x24BE + 0xBC = 0x257A -> 0xE780 -> 0b 1110 0111 1000 0000 -> ###..####.......
    <char65_bytes_row14>: offset = 0x257A + 0xBC = 0x2636 -> 0x0000 -> 0b 0000 0000 0000 0000 -> ................
    <char65_bytes_row15>: offset = 0x2636 + 0xBC = 0x26F2 -> 0x0000 -> 0b 0000 0000 0000 0000 -> ................
    <char65_bytes_row16>: offset = 0x26F2 + 0xBC = 0x27AE -> 0x0000 -> 0b 0000 0000 0000 0000 -> ................

  • Note: the character has an effective width of 9 pixels, which matches the corresponding value in the <char_widths> byte array, at index 33 (65-32), that is at offset 0x1B67

What we just accomplished is to decode the upper case 'A' of font set #4, the font that is used for the opening credits as shown below (note that Civ creates bevel effects by re-using the same fonts with 2 displacements, 1-pixel up and 1-pixel down):

attachment.php


Finally, for code enthusiasts out there, I also attach a Java class containing the routine to decode FONTS.CV, and used to generate the FONTS.CV snapshots shown in the threads below: View attachment TestFonts.java.zip
(you have to unzip it because of this forum's limitations on file attachments...)

Hope you'll enjoy !



-------- Hereunder is the content of what was the original post in this thread

I am actually in the very early process of trying to understand it, but looking at the bit-level binary X-ray of the file I generated (see below), I think the coding of fonts within this file is not using an intricate coding.

There is an obvious pattern of "font sets" separated by blank areas. At the beginning of each font set, a different pattern is visible , probably coding meta-information about the font (e.g. number of bits/pixels/width of each char in the font set).

Besides this, the first bytes of the file seem to be a series of 8 short integers, probably representing the number of font sets, as well as the offsets where a font set begins:

CivDOS FR 474.05

Code:
[0x0000] 07 00 98 00 20 05 88 08 4b 11 08 22 0c 2f 94 3e
[0x0010] ...

0x0007 =     7
0x0098 =   152
0x0520 =  1312
0x0888 =  2184
0x114b =  4427
0x2208 =  8712
0x2f0c = 12044
0x3e94 = 16020
EOF    = 19123

set 1:  1312 -   152 = 1160 bytes
set 2:  2184 -  1312 =  872 bytes
set 3:  4427 -  2184 = 2243 bytes 
set 4:  8712 -  4427 = 4285 bytes
set 5: 12044 -  8712 = 3332 bytes
set 6: 16020 - 12044 = 3976 bytes
set 7: 19123 - 16020 = 3103 bytes

CivDOS EN 475.01

Code:
[0x0000] 09 00 7c 00 e4 03 8c 06 2f 0f ac 1b 90 28 38 34
[0x0010] 60 3b 47 43 ...

I may be wrong... I'll keep you posted when I know more!

Enjoy
 

Attachments

  • FONTS.CV.png
    FONTS.CV.png
    10.8 KB · Views: 514
  • FONTS.CV.16.png
    FONTS.CV.16.png
    11.5 KB · Views: 358
  • FONTS.CV.set4.char65.opening_creds.png
    FONTS.CV.set4.char65.opening_creds.png
    16.5 KB · Views: 732
Looking at CivDOS EN 475.01 for the moment, some more analysis reveals a recurrent pattern:

Header:
1 unsigned short (little endian): number of font sets
<X> unsigned shorts (l-e) : offset in the file where the font set characters begin (<x> being the value of the first unsigned short)

Font set:
96 bytes: unknown usage, but guessed to be the width, in pixel of all of the 96 supported chars
2 bytes: first <AF> and last <AL> supported ASCII codes; throughout the file, their value is 0x20 (space) and 0x7F (last supported standard ASCII code); this covers all letters, upper and lower case, numbers, and punctuation; <AL>-<AF> is 0x7F - 0x20 +1 = 0x60 = 96 characters
1 byte: guessed to be the "number of bytes per char" ( <N> ), defining the structure of the sub-sequent charset data blocks
2 bytes: guessed to be the first (<F>) and last (<L>) indices of the sub-sequent charset data rows; I guess that <L>+1 may also be read as "char height" (seems that subsequent blocks are divided in rows...)
3 bytes: unknown usage, their value is almost always 0x01, 0x01, 0x00
( <AL> - <AF> + 1 ) x <N> x ( <L> - <F> + 1 ) bytes : guessed to be the actual charset pixel data, composed of sequential byte blocks of length (<AL> - <AF> + 1) x <N>, i.e. 96 x <N> bytes, i.e. <N> bytes per character, per row, and there are (<L> - <F> + 1 ) rows

Everything above is still a rough guess, untested, that came to me by looking at FONTS.CV in a hex editor, so it may well be wrong... Will tell more when actually tested :)

Also, the initial list of offsets does not actually point the first byte of a font set (1st byte of the 96 "width" bytes), but rather to the first byte of the font set's character bytes, by-passing all the font set structure definition... Don't know why yet...
 
And here we are, just needed to read bits from left to right instead of right to left:

CivDOS FR 474.05

attachment.php


CivDOS EN 475.01

attachment.php


Finally I may be able to correct the accents in the French version !!! I has always bothered me that acute accents looked grave and vice-versa... More importantly, I'll be able to improve the map rendering in JCivED :)

I'll update the 1st post later on to describe the exact format.
 

Attachments

  • FONTS.CV.clear.EN47501.png
    FONTS.CV.clear.EN47501.png
    10.6 KB · Views: 778
  • FONTS.CV.clear.FR47405.png
    FONTS.CV.clear.FR47405.png
    10.2 KB · Views: 711
Back
Top Bottom