KenshiDRK / XiView

FFXI UI & Icon Changes
95 stars 21 forks source link

Inconsistent background colours for some icons. #8

Open HealsCodes opened 1 year ago

HealsCodes commented 1 year ago

Recently a few developers from the ashita discord looked into display issues for certain status icons that only appear when XiView is in use. On of the issues is that some icons would display with a seemingly black background instead of transparent when used in addons.

image

After some investigation it turns out that not all icons have a true black background (FF000000) but some slight deviation from it (FF050505 among others) - if that could be fixed to be consistently black it would be great.

KenshiDRK commented 1 year ago

do you have a list of icons for it? Is it taking true black to set the alpha channel? all the icons should have an alpha channel

HealsCodes commented 1 year ago

If your source files have an alpha channel it very much looks like a bug in the importer tool. Are you perchance using the "FFXI Icon Change" utility that is available in Caradog's version of XIView? We're still checking how XI determines what counts as "background" but the way FFXI Icon Change updates the resources would not mark them as ARGB.

This is still mostly informative as it seems there are more than just colour issues with how the icons get imported into the DATs (which would be down to the tool, not your work on the icons).

Not an 100% exhaustive list because some icons cover most of the background areas that make nice check spots but here:

file, detected background colour:
10.bmp,0x010101
11.bmp,0x010101
114.bmp,0x050505
116.bmp,0x010101
119.bmp,0x050505
120.bmp,0x050505
121.bmp,0x050505
122.bmp,0x050505
123.bmp,0x050505
124.bmp,0x050505
125.bmp,0x050505
128.bmp,0x050505
129.bmp,0x050505
130.bmp,0x050505
131.bmp,0x050505
132.bmp,0x050505
133.bmp,0x050505
147.bmp,0x050505
15.bmp,0x010101
150.bmp,0x010101
151.bmp,0x010101
152.bmp,0x010101
154.bmp,0x010101
191.bmp,0x010101
192.bmp,0x050505
193.bmp,0x050505
194.bmp,0x050505
195.bmp,0x050505
196.bmp,0x050505
197.bmp,0x050505
198.bmp,0x050505
199.bmp,0x050505
200.bmp,0x050505
201.bmp,0x050505
214.bmp,0x050505
215.bmp,0x050505
216.bmp,0x050505
217.bmp,0x050505
22.bmp,0x020202
220.bmp,0x050505
221.bmp,0x050505
222.bmp,0x050505
233.bmp,0x050505
234.bmp,0x050505
249.bmp,0x050505
28.bmp,0x010101
287.bmp,0x010101
294.bmp,0x030303
31.bmp,0x010101
340.bmp,0x020202
342.bmp,0x050505
345.bmp,0x010101
346.bmp,0x050505
347.bmp,0x010101
348.bmp,0x010101
350.bmp,0x040404
351.bmp,0x040404
376.bmp,0x030303
405.bmp,0x050505
408.bmp,0x010101
411.bmp,0x050505
42.bmp,0x050505
43.bmp,0x050505
431.bmp,0x010101
438.bmp,0x040404
441.bmp,0x050505
443.bmp,0x020202
446.bmp,0x040404
447.bmp,0x030303
45.bmp,0x050505
453.bmp,0x050505
454.bmp,0x050505
456.bmp,0x040404
457.bmp,0x050505
459.bmp,0x050505
46.bmp,0x050505
464.bmp,0x010101
466.bmp,0x030303
467.bmp,0x050505
468.bmp,0x0b0b0b
472.bmp,0x050505
477.bmp,0x050505
479.bmp,0x030303
482.bmp,0x020202
483.bmp,0x050505
485.bmp,0x050505
52.bmp,0x030303
53.bmp,0x010101
54.bmp,0x020202
55.bmp,0x050505
56.bmp,0x020202
57.bmp,0x050505
58.bmp,0x050505
583.bmp,0x010101
584.bmp,0x010101
61.bmp,0x050505
617.bmp,0x050505
62.bmp,0x050505
622.bmp,0x050505
623.bmp,0x010101
624.bmp,0x010101
63.bmp,0x050505
68.bmp,0x020202
74.bmp,0x050505
75.bmp,0x010101
76.bmp,0x030303
80.bmp,0x050505
81.bmp,0x050505
82.bmp,0x050505
83.bmp,0x050505
84.bmp,0x050505
85.bmp,0x050505
86.bmp,0x050505
87.bmp,0x020202
88.bmp,0x010101
89.bmp,0x010101
91.bmp,0x050505
93.bmp,0x010101
KenshiDRK commented 1 year ago

Yea I use the ffxi icon changer, but looking at the id most (if not all) of those icons were not made by me, while others like 619, 630, 631 I did myself are not there. Anyway is goin to take lot of time to fix it.

ThornyFFXI commented 1 year ago

There is a secondary issue here, in that the dat itself is not having the image size set correctly. The game doesn't care, but attempts to utilize the dat would. I understand this isn't really your doing; it's fault in the utility you've been using that far predates your involvement in the project, and appreciate your willingness to look at this issue. I wrote a utility that does the following to the dat:

-Converts 8 bit per pixel images to 32 bit per pixel, in the same format as the rest. This solves the issue of ffxi icon changer showing and exporting distorted images without altering the icons themselves.

-Converts any pixel where alpha is less than 0x80 and red,green,blue are less than 10 to 0x00000000 (true transparent) so that the images are properly compatible with typical graphics APIs. Note that the way FFXI loads them is using an alpha blender, so many of these not-quite-transparent pixels in the images are transparent when FFXI's alpha blend checks, but not when loaded through other applications.

-Adjusts the image size in the dat to be correct.

I have ran it on both your 57.DAT and 12.DAT and do not see any visible issues with the output, but if it were possible to have you review the images for distortion and either incorporate the utility into your release process or make the filtered dats available, it would be appreciated. If you have discord, I can be reached at Thorny#2936 (at least until discord changes it).

The filtered dats are attached. StatusIconDat.zip

KenshiDRK commented 1 year ago

That would be awesome, I'll reach you when I get some free time. I wonder if this will solve the issue with some icons not showing on the ffxi client sometimes.

ThornyFFXI commented 1 year ago

The icons not showing on the ffxi client are due to failure to load from disc, so I doubt it'll have much of any impact there. The main benefit is that because Ashita has the ability to pull the icons directly out of the dat, people who are using your mod will also have the modded icons applied to addons or plugins that display those dat-sourced icons. I'm not sure if this is something windower has any plans to add or support, so it would explain why it's gone unnoticed so long. There is some minor benefit in the iconchanger utility actually showing and exporting the corrupted ones after running the filter, but more just a matter of doing things correctly.

I'm playing with the filter conditions still to try to create a least-possible-impact run through, since it's very hard to tell if any individual pixel was altered, but it seems like I can probably drop the condition to just opacity below 0x10, and still get proper transparency. Granted, again, I didn't see any visible difference in the icons with the more aggressive filter. Worth noting that while FFXI is honoring the alpha channel due to the sloppy way they load it, the image headers being used both by the original dat and the one generated by ffxi icon changer are RGB32 (which by spec does not have an alpha channel, and signifies that byte should be irrelevant).

atom0s commented 1 year ago

Hey there, figured I should share the information I reversed for this stuff that we discussed in the Ashita Discord here too. There are a few aspects to this stuff that has some 'bugs' and potential fixes that can make the DATs 'better' for more than just the clients direct usage.

First, the list of DATs that the client makes use of for the status icons are:

ROM/287/80.DAT -- NQ Icons (16x16, Type 1, Japanese)
ROM/287/81.DAT -- NQ Icons (16x16, Type 1, English)

ROM/0/12.DAT   -- HQ Icons (32x32, Type 2, Japanese)
ROM/119/57.DAT -- HQ Icons (32x32, Type 2, English)

The client handles the lookups of these DAT files to determine which one to load based on a few internal lookup tables. The top-most table is just a list of indexes that are used with another table. Currently, that list looks like this:

constexpr uint32_t g_statusicon_lookup_idx[] = {
    0x39,
    0x38,
    0x00,
};

These indexes are then used to pull a DAT id from a language specific based table that the client uses for a handful of base/common DATs. (Things like items, general string tables, various static menu data, etc.) In this case, these indexes would point to the following values:

Using Japanese Lookup Table:

  - 0x38 would return DAT id: 12 (`ROM/0/12.DAT`)
  - 0x39 would return DAT id: 39556 (`ROM/287/80.DAT`)

Using English Lookup Table:

  - 0x38 would return DAT id: 87 (`ROM/119/57.DAT`)
  - 0x39 would return DAT id: 39557 (`ROM/287/81.DAT`)

Next, onto some of the bugs being reported here.


1. Invalid/Incorrect Bitmap Image Sizes

To better explain the issue, it's important to note how the FFXI client handles most bitmap data inside of its DAT files. The main thing is that the client stores bitmap images with the BITMAPFILEHEADER stripped from the data. This leaves the client responsible for rebuilding it on the fly as needed. For this to function fully/properly, they generally create custom structures that hold information about the bitmap as well as the BITMAPINFOHEADER and following pixel data. In most cases, this can be as simple as just storing the image size in a separate field that would have been populated in the BITMAPFILEHEADER structure.

For status icons, the layout of the structure used by the client is:

struct statusiconentry_t
{
    uint8_t     data0000_[0x01FC];      // The status icons information block. (Encoded.)
    uint8_t     data0001_[0x0084];      // The status icons information block. (Unused.)
    uint32_t    image_size_;            // The status icons size.
    uint8_t     image_type_;            // The status icons bitmap pixel type.
    uint8_t     image_name_[0x0010];    // The status icons name.
    uint8_t     bitmap_[0x156A];        // The status icons bitmap and pixel information.
    uint8_t     end_of_entry_;          // 0xFF - End of entry.
};

To make use of an example when explaining this bug, I'll make use of the retail version of ROM/119/57.DAT using the first icon in the file 0.

Parsing this file manually using the statusiconentry_t structure outline I showed above will give the following information for the default retail icon:

The image_type_ value is a bitfield of sorts that holds multiple values. The client makes use of this value to pull the following two things from it:

The client then uses these values to tell how the image should be loaded. (This is all done within YmIcon::YmIcon in the client.) For the first icons information, both val1 and val2 here would result in 1. The client then loads additional data from the BITMAPINFOHEADER (mainly biWidth, biHeight, biCount, and biClrImportant) to determine how to properly read the pixel data from the image.

In this case with the first icon, the code route taken will result in:

    switch ( (image_size_ >> 4) & 7 )
    {
        case 0:
        case 1:
            goto LABEL_21;

    // snipped unused code..

LABEL_21:
            switch ( icon->biCount )
            {

    // snipped unused code..

                case 32:
                    i = &pixels[icon->biWidth * icon->biHeight];
                    break;

    // snipped unused code..

pixels here being an unsigned integer* array.

To demonstrate a bug with the icon size, you can do the following steps with the Icon Changer tool:

Meanwhile the pixel data is exactly the same and the other information about the icon has not changed either. This causes any other tool that may try to parse this DAT file in a 'correct' manner to fail to read the icons correctly as the image_size_ field being given is wrong.

For the actual retail client, they don't really care about the size field and generally ignore it. Instead, they manually determine the proper size of the pixel data/array based on the other bitmap header information. This is a valid means to determine how to read the pixel block, but it's not the only valid way to do so. It is still ideal to properly repopulate the DAT file fully when rewriting information so that other things can make use of the data correctly.


2. Bad Transparent Black Values

This is not so much a 'bug' but rather something that could be worked on to better support other tools/code bases when trying to make use of the bitmaps from the status icon DAT files. The stock icons from SE make use of the alpha and black color (generally solid black) to allow the icons to be properly transparent outside of how they personally render them. Meaning, other tools loading the icons will see the 'black' spots in the bitmaps as transparent correctly as well. This allows for direct single-color keying against black (#000000) to show just the icons and not the filler areas.

With XiView, the colors are not fluid as Shirk mentioned above. Instead, many of the edited icons are using several black-like colors (#0x010101 through 0x050505) which cause the single-color keying some other tools and rendering setups use to fail. This causes icons to be patchy or show blotches of black that would otherwise be transparent. (See Shirks first post image, the 7th icon has the black blotch around and above 'SP' which should not be there, but due to the colors used it cannot be single-color keyed.)

To explain why this works fine in the FFXI client itself, I can share parts of the code used to render the status icons: (Taken from inside of YmIcon::Draw)

PTR_g_pDx->D3D8Dev->SetTexture(PTR_g_pDx->D3D8Dev, 0, texture);
if ( a4 != 1.0 )
{
    PTR_g_pDx->D3D8Dev->SetTextureStageState(PTR_g_pDx->D3D8Dev, 0, D3DTSS_MINFILTER, 2);
    PTR_g_pDx->D3D8Dev->SetTextureStageState(PTR_g_pDx->D3D8Dev, 0, D3DTSS_MAGFILTER, 2);
    PTR_g_pDx->D3D8Dev->SetRenderState(PTR_g_pDx->D3D8Dev, D3DRS_ALPHAREF, 96);
}

// snipped vertices initialization..

PTR_g_pDx->D3D8Dev->SetVertexShader(PTR_g_pDx->D3D8Dev, D3DFVF_TEX1 | D3DFVF_DIFFUSE | D3DFVF_XYZRHW);
FUNC_CDx_DrawPrimitiveUP(PTR_g_pDx, D3DPT_TRIANGLESTRIP, 2, vertices, 0x1C);

if ( a4 != 1.0 )
{
    PTR_g_pDx->D3D8Dev->SetTextureStageState(PTR_g_pDx->D3D8Dev, 0, D3DTSS_MINFILTER, 2);
    PTR_g_pDx->D3D8Dev->SetTextureStageState(PTR_g_pDx->D3D8Dev, 0, D3DTSS_MAGFILTER, 1);
    PTR_g_pDx->D3D8Dev->SetRenderState(PTR_g_pDx->D3D8Dev, D3DRS_ALPHAREF, 0);
}

When the FFXI client is rendering the status icons, it makes use of Direct3D's built in alpha blending/testing through the use of the D3DRS_ALPHAREF and D3DRS_ALPHAFUNC render states. In this case, D3DRS_ALPHAFUNC is already set from a previous call which has it set to D3DCMP_GREATER or D3DCMP_GREATEREQUAL. However, there is some 'leeway' in how Direct3D blends based on this. Here it is using 96 as the alpha ref value, but in practice they begin the blending at around 89/90ish, and have it 'fully' blended in at 105-110ish. (Side effect from this is that the alpha values that are at that lower scale of the values around 90 will cause some checkerboarding to happen. See here: https://i.imgur.com/oCcGYwE.png)

The issue we see from using XiView is that other rendering setups, such as some of things in Ashita and Windower, is that they make use of several texture loading calls built into the extended Direct3D SDK, such as:

Each of these calls offers a means to make a single color transparent via color keying. But because it only offers a single color, it means that anything close to the desired color is not also marked. It has no means to use a similar setup to the alpha ref render state approach. This is why things like what Shirk showed in her first image (icon 7) happen. The outer black was properly true #000000 black, but other parts of the icon were not and fell into that #010101 to #050505 range which wont trigger the keying.


In regards of what to do from here, as-is Icon Changer is obviously bugged in some regards. I don't know if this tool is open source directly, but it is written in a .NET language (such as C# or Vb.NET) so it can be decompiled and rebuilt with fixes/changes.

Or if you would be more interested in a new tool being built to deal with this kind of project. There are better ways to go about doing a full DAT rebuild like this instead of how Icon Changer works since its more of a point-and-click to replace each icon one by one.

Either way, thank you for what you do with this project. :)

KenshiDRK commented 1 year ago

Yea, its open source, guess the issue should be here https://github.com/Caradog/XI-View/blob/87d40d3b56aecb8557ed4cf74deede0ca5761ec0/03%20-%20Utilities/FFXI%20Icon%20Change/Source/FFXI-IconChange%203.3/FFIcon.cs

I'll take a look, and try to make sense of it after this weekend.

KenshiDRK commented 1 year ago

The image_type_ value is a bitfield of sorts that holds multiple values. The client makes use of this value to pull the following two things from it:

  • val1: image_size_ >> 7
  • val2: (image_size_ >> 4) & 7

Is this right? if image_size_ is 4153 I get 32 foir val1 and 3 for val2, shouldn't it be image_type_ instead of image_size_?

atom0s commented 1 year ago

Sorry that was a typo. That should be image_type_ for those not image_size_.

KenshiDRK commented 1 year ago

I think I found the issue, https://github.com/Caradog/XI-View/blob/master/03%20-%20Utilities/FFXI%20Icon%20Change/Source/FFXI-IconChange%203.3/FFIcon.cs#L208

Changing this line to mBytes[OFFSET_IMAGE + 1] = 0x10; replaces the icon with the correct size, it still saves icons to drive with wrong size but not sure how to solve this and its a minor issue. Not sure if more modifications are needed, other than the blacks on a bunch of icons.

HealsCodes commented 10 months ago

I think this has been dealt with to everyone's satisfaction?