Kazuma Posted December 17, 2023 Share Posted December 17, 2023 (edited) Hi, can someone help me make a bms script that could convert a pzx file to png or jpg? I tried making on my own using this guide (https://www.psxhax.com/threads/quickbms-making-bms-scripts-tutorial-by-thattruestruggle.2190/) but I couldn't find the header and offset. Update: I tried decompressing 005.pzx on face zip using someone's script (https://ya4r.net/forum/index.php?id=156605&page=1&newup) assuming that it is also a zlib since it just for the same game and it converted to 00000135.dat. face.zip dat&bms.zip Edited December 18, 2023 by Kazuma Update Link to comment Share on other sites More sharing options...
piken Posted December 18, 2023 Share Posted December 18, 2023 (edited) I don't have your answer, just an observation. Looking at the corresponding 00000135.dat sample you shared via the XeNTaX discord (which I recommend amending into the .zip file above to facilitate broader potential assistance here, plus a link directly to the .bms file you used), it's clear that the data isn't completely decompressed yet into a regular linear image, as there are variable size rows. What's curious is instances of {FE FF 00 00 ## 80}, and that often the width varies dependent on the 3rd and 5th byte, like they might be padding values for the left and right edges of each row. 🤔 If you wrap all the byte spans between FE FF into rows, then you notice something more graphicsish. Though, without a larger image sample and correct palette, I can't tell what I'm looking at. 🤷 Edited December 18, 2023 by piken Link to comment Share on other sites More sharing options...
Kazuma Posted December 18, 2023 Author Share Posted December 18, 2023 13 minutes ago, piken said: I don't have your answer, just an observation. Looking at the corresponding 00000135.dat sample you shared via the XeNTaX discord (which I recommend amending into the .zip file above to facilitate broader potential assistance here, plus a link directly to the .bms file you used), it's clear that the data isn't completely decompressed yet into a regular linear image, as there are variable size rows. What's curious is instances of {FE FF 00 00 ## 80}, and that often the width varies dependent on the 3rd and 5th byte, like they might be padding values for the left and right edges of each row. 🤔 If you wrap all the byte spans between FE FF into rows, then you notice something more graphicsish. Though, without a larger image sample and correct palette, I can't tell what I'm looking at. 🤷 Thank you! That's an interesting progress, the picture shown looks like her. I'm not sure if this is the color palette but some pzx file has the same name file included but with different extension (.mpl). map.zip Link to comment Share on other sites More sharing options...
Solution piken Posted December 18, 2023 Solution Share Posted December 18, 2023 (edited) > the picture shown looks like... Indeed, with that reference image, I could tell byte offset 2 in each row header holds the left padding value, which helps the staff look like a staff. struct RowHeader { uint8 signature0; // 0xFE uint8 signature1; // 0xFE uint8 leftRowPadding; uint8 unknown; // Zero uint8 maybeRightRowPadding; } > some pzx file has the same name file included but with different extension (.mpl). Hmm, it doesn't look like a palette trying any of the usual formats (r5g5b5, b8g8r8a8, r8g8b8), but I see how the extension "mpl" and small file size (<1024 bytes) hints at that. Perhaps they too are compressed. Well, I'll defer the palette investigation to you, but here's some wonderfully hacky non-idiomatic Python code (3.9.17) to wrap the input data and output to 8bpp grayscale images: import sys import os from PIL import Image # Depends on Pillow (I used 10.0.0) if len(sys.argv) < 6: print( "Need the following parameters:\n" "- input file name (e.g. foo.bin)\n" "- output file byte width (e.g. 256)\n" "- marker bytes to find and split input into rows (e.g. FE,FF)\n" "- byte size of each row header in input to read first pixel\n" "- left padding offset (where the left padding value resides each block)\n" "\n" " python wrapDataOnMarker.py filename.dat 256 FE,FF 6 2\n" "\n" "Pass left padding offset = 0 for none.\n" "Note this only works for 8bpp pixels.\n" "Produces output \"filename.dat.wrapped\" and \"filename.dat.png\"." ) exit() #endif inputFilename = sys.argv[1] outputDataFilename = inputFilename + ".wrapped" outputImageFilename = inputFilename + ".png" maximumWidth = int(sys.argv[2]) splitMarkerText = sys.argv[3].split(',') rowHeaderByteSize = int(sys.argv[4]) leftPaddingOffset = int(sys.argv[5]) if rowHeaderByteSize < len(splitMarkerText): raise ValueError("The row header byte size needs to be at least the size of the split marker.") #endif if leftPaddingOffset >= rowHeaderByteSize: raise ValueError("The left padding must reside within the row header byte size.") #endif # Convert comma separated hex codes to byte array. # (and no, I don't really know Python, as there is likely a shorter way) splitMarkerText = sys.argv[3].split(',') splitMarkerBytes = bytearray() for c in splitMarkerText: splitMarkerBytes.append(int(c, 16)) #endfor splitMarkerBytesLength = len(splitMarkerBytes) print(f"Input filename: {inputFilename}") print(f"Maximum width: {maximumWidth}") print(f"Split marker: {splitMarkerText}") wrappedData = bytearray() # Split every occurence of the marker into a separate row. with open(inputFilename, 'rb') as f: fileData = f.read() fileLength = len(fileData) print(f"File length: {fileLength}") previousWrappedStart = 0 rowCount = 0 fileOffset = 0 paddingWidthLeft = 0 lastFileOffset = max(fileLength - rowHeaderByteSize, 0) while fileOffset < fileLength: #print(fileOffset, fileData[fileOffset : fileOffset + splitMarkerBytesLength]) if fileData[fileOffset : fileOffset + splitMarkerBytesLength] == splitMarkerBytes: # Beginning new row since match was found... # Compute the 3 spans of the row: left padding | interior pixel width | right padding. byteSpanWidth = fileOffset - previousWrappedStart if leftPaddingOffset > 0 and fileOffset + leftPaddingOffset < fileLength: paddingWidthLeft = fileData[fileOffset + leftPaddingOffset] #endif clampedPaddingWidthLeft = min(paddingWidthLeft, maximumWidth) interiorWidth = byteSpanWidth - rowHeaderByteSize clampedInteriorWidth = min(interiorWidth, maximumWidth) clampedWidthLeftAndInterior = min(clampedInteriorWidth + clampedPaddingWidthLeft, maximumWidth) clampedInteriorWidth = clampedWidthLeftAndInterior - clampedPaddingWidthLeft clampedPaddingWidthRight = maximumWidth - clampedWidthLeftAndInterior print(f"matching byte offset: {fileOffset} previous byte offset: {previousWrappedStart}, byte span width: {byteSpanWidth}") print(f" emitting row: {rowCount}, padding left: {paddingWidthLeft}, padding right: {clampedPaddingWidthRight}, interior width: {interiorWidth}") print(f" previous output data byte size: {len(wrappedData)}") #print(f" widthInterior: {widthInterior}, paddingWidthLeft: {paddingWidthLeft}, widthLeftAndInterior: {widthLeftAndInterior}") wrappedData.extend([0 for _ in range(clampedPaddingWidthLeft)]) rowStartOffset = previousWrappedStart + rowHeaderByteSize wrappedData += fileData[rowStartOffset : rowStartOffset + clampedInteriorWidth] wrappedData.extend([0 for _ in range(clampedPaddingWidthRight)]) previousWrappedStart = fileOffset fileOffset += rowHeaderByteSize rowCount += 1 else: fileOffset += 1 #endif #endfor #endwith print(f"Total row count: {rowCount}") # Write wrapped data file. if rowCount == 0: print("No instances of the split marker were found. Skipping file write.") else: with open(outputDataFilename, 'wb') as f: f.write(wrappedData) #endwith #endif # Write 8bpp image (could use palette mode "P" if the palette format was known). image = Image.frombytes("L", [maximumWidth, rowCount], wrappedData) image.save(outputImageFilename) Edited December 18, 2023 by piken Link to comment Share on other sites More sharing options...
Kazuma Posted December 18, 2023 Author Share Posted December 18, 2023 (edited) On 12/18/2023 at 8:17 PM, piken said: Well, I'll defer the palette investigation to you, but here's some wonderfully hacky non-idiomatic Python code (3.9.17) to wrap the input data and output to 8bpp grayscale images: Thanks! Just one last question, how to use this script? Do I just execute it with cmd or does it need quickbms? Edited December 22, 2023 by Kazuma Solved Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now