Skip to content

Easier way to make icons #1

Closed
Closed

Description

@Sakitoshi

Since I plan on using your better looking icons to replace the ones made with bmp2icon that I've been using until now.
Image

I made a small utility to speed up the process and avoid the need to use a 3D modeling program to replace the texture.
The texture is stored in plain bitmap BGR555 and flipped vertically at offset 0x398 with a length of 0x8000.
Image

So, it is fairly simple to inject a new texture by preparing your new texture swapping red and blue and flipping the image vertically and then saving as a BMP RGB555 (or X1 R5 G5 B5) then getting rid of the BMP header and injecting the raw bitmap.
Image

My utility writes the first part of the 3D icon, then uses imagemagick to take care of the texture and finally spits out a PS2 3D icon, so making textures following your template is all that is needed.

I wrote it using autohotkey, but I imagine it should be easy to port to python since the heavy lifting is done by imagemagick.
Simply place magick.exe alongside the utility and it should work.
example: ps2iconmaker.exe texture_image.png -o ps2icon.icn
ps2iconmaker.zip

Note that this only generates PS2 DVD case icons since that is what I'm most interested in.
The variable iconGeo contains the geometry of the icon. So, changing that with the geometry of the other icons would make it generate the rest.

;@Ahk2Exe-ConsoleApp
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
; SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
#ErrorStdOut

global argc
argc = %0%
global argv := []
Loop, % argc
{
argv[A_Index] := %A_Index%
}

If (argc = "0")
    {
    FileAppend,
    (
PS2 Icon Maker

Usage: ps2iconmaker <image> [-o output]

<image>: an image that is 128x128 in size to use as a texture
         for more details check the template
[-o output]: filename of the resulting PS2 icon

If an output name is not given, the generated icon will be named ps2icon.ico
    ),*
    ExitApp
    }

If RegExMatch(argv[2], "i)^-o$",, 1) and (argc = 2)
    {
    FileAppend, You need to specify an output filename when using switch -o.`n,**
    ExitApp
    }
    
If (! RegExMatch(argv[2], "i)^-o$",, 1)) and (argc > 1)
    {
    FileAppend, Specify only one input image at a time.`n,**
    ExitApp
    }

If CheckMagick()
    {
    FileAppend, magick.exe is missing.`n,**
    ExitApp
    }

texture := ProcessImage(argv[1])
If (! texture = "temp.bmp")
    {
    FileAppend, %texture%`n,**
    ExitApp
    }

If MakeIcon(texture, argv[3])
    FileAppend, %errors%`n,**
else
    FileAppend, Done.`n,*

ExitApp

;
; Functions
;
CheckMagick()
{
If ! FileExist(A_ScriptDir . "\magick.exe")
    Return, 1

Return, 0
}

ProcessImage(img)
{
testimg := FileOpen(img, "r")
If ! IsObject(testimg)
    Return, "Cannot open " img "."
testimg.Close()

tempTex := "temp.bmp"
Magick = "%A_ScriptDir%\magick.exe" "%img%" -flip -channel-fx "red <=> blue" -alpha off -define bmp:format=bmp4 -define bmp:subtype=RGB555 "%tempTex%"
RunWait, %Comspec% /c "%Magick%"

Return, tempTex
}

MakeIcon(BMP, output="")
{
If (output = "")
    output := "ps2icon.ico"

IconGeo =
(
0000010001000000060000000000803F
2400000072E98EB91C0D0004000097FB
9FF00004030006007F7F7F8072E950F9
D5FA0004000097FB9FF000040300B90B
7F7F7F80BA178EB91C0D0004000097FB
9FF00004130806007F7F7F8072E950F9
D5FA0004000097FB9FF000040300B90B
7F7F7F80BA1750F9D5FA0004000097FB
9FF000041308B90B7F7F7F80BA178EB9
1C0D0004000097FB9FF0000413080600
7F7F7F80BA178EB91C0D000400100000
0000000446027C0E7F7F7F80BA1750F9
D5FA00040010000000000004030E7C0E
7F7F7F80BA17CFBA7B11000400100000
000000044602A00D7F7F7F80BA1750F9
D5FA00040010000000000004030E7C0E
7F7F7F80BA1791FA33FF000400100000
00000004030EA00D7F7F7F80BA17CFBA
7B11000400100000000000044602A00D
7F7F7F80BA17CFBA7B11000400006904
610F0004E90700007F7F7F80BA1791FA
33FF000400006904610F0004E907B90B
7F7F7F8072E9CFBA7B11000400006904
610F0004FC0F00007F7F7F80BA1791FA
33FF000400006904610F0004E907B90B
7F7F7F8072E991FA33FF000400006904
610F0004FC0FB90B7F7F7F8072E9CFBA
7B11000400006904610F0004FC0F0000
7F7F7F8072E9CFBA7B11000400F00000
00000004030E390C7F7F7F8072E991FA
33FF000400F00000000000043C02390C
7F7F7F8072E98EB91C0D000400F00000
00000004030E290D7F7F7F8072E991FA
33FF000400F00000000000043C02390C
7F7F7F8072E950F9D5FA000400F00000
000000043C02290D7F7F7F8072E98EB9
1C0D000400F0000000000004030E290D
7F7F7F8072E9CFBA7B11000400009FF0
690400044904C00F7F7F7F8072E98EB9
1C0D000400009FF0690400044904E60E
7F7F7F80BA17CFBA7B11000400009FF0
69040004590CC00F7F7F7F8072E98EB9
1C0D000400009FF0690400044904E60E
7F7F7F80BA178EB91C0D000400009FF0
69040004590CE60E7F7F7F80BA17CFBA
7B11000400009FF069040004590CC00F
7F7F7F8072E950F9D5FA00040000610F
98FB00044304E30E7F7F7F8072E991FA
33FF00040000610F98FB00044304C60F
7F7F7F80BA1750F9D5FA00040000610F
98FB0004630CE30E7F7F7F8072E991FA
33FF00040000610F98FB00044304C60F
7F7F7F80BA1791FA33FF00040000610F
98FB0004630CC60F7F7F7F80BA1750F9
D5FA00040000610F98FB0004630CE30E
7F7F7F80010000000100000000000000
00000000010000000000000001000000
000000000000803F
)

IconGeo := StrReplace(IconGeo, "`n")
IconLen := StrLen(IconGeo) / 2
Hex2Bin(IconData, IconGeo, IconLen)
BMPFile := FileOpen(BMP, "r")
If ! IsObject(BMPFile)
    Return, "Cannot open temporary image."
BMPFile.Seek(0xA)
BMPFile.RawRead(BMPStart, 0x4)
BMPStart := "0x" ChangeEndianess(Bin2Hex(BMPStart, 4))
BMPFile.Seek(BMPStart)
BMPFile.RawRead(BMPTex, 0x8000)
BMPFile.Close()
FileDelete, % BMP
FileDelete, ps2icon.ico
IconICO := FileOpen(output, "a")
If ! IsObject(IconICO)
    Return, "Cannot open " output " for writing."
IconICO.RawWrite(IconData, IconLen)
IconICO.RawWrite(BMPTex, 0x8000)
IconICO.Close()

Return, 0
}

Bin2Hex(bin, _byteNb=0)
{
    local intFormat, dataSize, dataAddress, granted, x

    ; Save original integer format
    intFormat = %A_FormatInteger%
    ; For converting bytes to hex
    SetFormat Integer, Hex

    ; Get size of data
    dataSize := VarSetCapacity(bin)
    if (_byteNb < 1 or _byteNb > dataSize)
        _byteNb := dataSize
    dataAddress := &bin
    ; Make enough room (faster)
    granted := VarSetCapacity(hex, _byteNb * 2)
    if (granted < _byteNb * 2)
    {
        ; Cannot allocate enough memory
        ErrorLevel = Mem=%granted%
        Return -1
    }
    Loop %_byteNb%
    {
        ; Get byte in hexa
        x := *dataAddress + 0x100
        StringRight x, x, 2 ; 2 hex digits
        StringUpper x, x
        hex = %hex%%x%
        dataAddress++   ; Next byte
    }
    ; Restore original integer format
    SetFormat Integer, %intFormat%

    Return hex
}

Hex2Bin(ByRef @bin, _hex, _byteNb=0)
{
	local dataSize, granted, dataAddress, x

	; Get size of data
	x := StrLen(_hex)
	dataSize := Ceil(x / 2)
	if (x = 0 or dataSize * 2 != x)
	{
		; Invalid string, empty or odd number of digits
		ErrorLevel = Param
		Return -1
	}
	If (_byteNb < 1 or _byteNb > dataSize)
	{
		_byteNb := dataSize
	}
	; Make enough room
	granted := VarSetCapacity(@bin, _byteNb, 0)
	if (granted < _byteNb)
	{
		; Cannot allocate enough memory
		ErrorLevel = Mem=%granted%
		Return -1
	}
	dataAddress := &@bin

	Loop Parse, _hex
	{
		if (A_Index & 1)	; Odd
		{
			x = %A_LoopField%	; Odd digit
		}
		else
		{
			; Concatenate previous x and even digit, converted to hex
			x := "0x" . x . A_LoopField
			; Store integer in memory
			DllCall("RtlFillMemory"
					, "UInt", dataAddress
					, "UInt", 1
					, "UChar", x)
			dataAddress++
		}
	}

	Return _byteNb
}

ChangeEndianess(hex_in)
{
    hex_len := StrLen(hex_in) / 2
    Loop, % hex_len
    {
        hex_out .= SubStr(hex_in, 1 + (A_Index * -2), 2)
    }

    Return, hex_out
}

Activity

  1. CosmicScale commented on May 4, 2025

    @CosmicScale
    Owner

    Well, that is awesome! Could you please get this working for the other icon types too? Hopefully, we'll see more people making icons if the process is made easier :)

  2. Sakitoshi commented on May 4, 2025

    @Sakitoshi
    ContributorAuthor

    sure.
    ps2iconmaker_v1.1.zip

    it works the same as before but added the -t switch to specify an icon type to generate.
    just copy imagemagick executable and you are good to go.
    ahk source is included in the zip.

    ps1 cd case and pal/jp case were the same as ps2 dvd case, with the texture at offset 0x398.
    only the ps1 multidisc case was different, i suppose it has more triangles or texture coordinates specified because the texture was further down at offset 0x21F8, same size though. not a huge problem, but worth noting.

  3. Sakitoshi commented on May 5, 2025

    @Sakitoshi
    ContributorAuthor

    dusted off my fedora laptop and I'm working on a linux script to do the same since imagemagick is on linux too.

  4. Sakitoshi commented on May 5, 2025

    @Sakitoshi
    ContributorAuthor

    and here it is.
    ps2iconmaker_linux.zip

    it has the same functionality as the windows version.
    it uses imagemagick to convert the image to bitmap then dd to extract the relevant part of the image, after that it concatenates one of the included .icn files, depending on the icon type, with a temporary .tex file to get the desired icon.
    so, having imagemagick installed is the only thing needed for it to work.

    #!/bin/sh
    version="1.1"
    help="PS2 Icon Maker v$version
    
    Usage: ps2iconmaker <image> [-o output] [-t icon type] [-?]
    
    <image>: an image that is 128x128 in size to use as a texture
             for more details check the templates
    [-o output]: filename of the resulting PS2 icon
    [-t icon type]: type of icon to generate
                    1 - PS1 CD case
                    2 - PS2 DVD case
                    3 - PS1 PAL/JP case
                    4 - PS1 multi-disc case
    [-?]: shows this help
    
    If an output name is not given, the generated icon will be named ps2icon.ico
    If an icon type is not given, a PS2 DVD case icon will be generated"
    
    if [ -z $1 ]; then
      echo "$help"
      exit 0
    else
      input="$1"
    fi
    
    while [[ $# -gt 0 ]]; do
      case $1 in
        -o)
          output="$2"
          shift
          ;;
        -t)
          type="$2"
          shift
          ;;
        -?)
          echo "$help"
          exit 0
          ;;
        *)
          args+=("$1")
          shift
          ;;
      esac
    done
    
    if [ -z $output ]; then
      output="ps2icon.ico"
    fi
    
    case $type in
      1)
        icon="ps1cdusicon.icn"
        ;;
      2)
        icon="ps2dvdicon.icn"
        ;;
      3)
        icon="ps1cdpaljpicon.icn"
        ;;
      4)
        icon="ps1multidiscicon.icn"
        ;;
      *)
        type="2"
        icon="ps2dvdicon.icn"
        ;;
    esac
    
    if ! command -v magick > /dev/null 2>&1 ; then
      echo "magick not found."
      exit 1
    fi
    
    magick "$input" -flip -channel-fx "red <=> blue" -alpha off -define bmp:format=bmp4 -define bmp:subtype=RGB555 temp.bmp > /dev/null 2>&1
    dd bs=1 if=temp.bmp of=temp.tex skip=138 count=32768 iflag=skip_bytes,count_bytes > /dev/null 2>&1 &&
    cat "$icon" temp.tex > "$output" &&
    rm temp.bmp temp.tex
    echo done.
    exit 0
    
  5. CosmicScale commented on May 6, 2025

    @CosmicScale
    Owner

    Amazing! Thank you. When I get some time I'll give them a spin and add them to the templates download :)

  6. CosmicScale commented on Jun 13, 2025

    @CosmicScale
    Owner

    @Sakitoshi I just pushed a PSBBN update! With the help of your script, the game installer now automatically creates HDD-OSD icons. If a game's cover exists in the OPL Manager Art Database, an icon will be generated. All new icons are automatically contributed to the HDD-OSD Icon Database!

  7. Sakitoshi commented on Jun 13, 2025

    @Sakitoshi
    ContributorAuthor

    pretty cool.
    i thought about it but couldn't think of a good way to determine when the spine starts and ends on the image. so, i gave up on the idea.

  8. CosmicScale commented on Jun 13, 2025

    @CosmicScale
    Owner

    pretty cool

    I'm looking forward to seeing how many icons get generated this weekend!

  9. CosmicScale commented on Jan 2, 2026

    @CosmicScale
    Owner

    Finally got around to creating a repository for the Icon Generator. I decided to keep it all bash as it works well for Windows users when run from a batch file, just as long as they already have the PSBBN Definitive Patch installed on their PC.

    You can find the new icon generator here. This will make generating missing icons much easier. Thanks again for the initial scripts! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions