Last active
February 8, 2019 00:07
-
-
Save mod-san/d2e36b62ec2fa32c4ccb3df4ebbda886 to your computer and use it in GitHub Desktop.
Convert SEGA Dreamcast PVR images to PNG ones (of an entire directory, recursively), using Pvr2Png.exe and and the Ruby gem chunky_png. [It has specifically been used to convert Shenmue's PVR files to PNGs.]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "chunky_png" | |
class Dir | |
def self.each_valid_file(path = "**/*") | |
glob(path) { |f| | |
File.directory?(f) or f == File.basename(__FILE__) or yield f | |
} | |
end | |
def self.exist_or_make(name) | |
File.exist?(name) or mkdir(name) | |
end | |
end | |
class File | |
def self.pvrDissect(f) | |
string = IO.binread(f); stringSize = string.size; curOffset = 0 | |
while curOffset < stringSize | |
curOffset = string.index("PVRT", curOffset) or break; curOffset += 4 | |
pvrSize = string[ curOffset...(curOffset + 4) ].unpack("H*")[0].split(/(\d{2})/).reject(&:empty?).reverse.join.hex | |
pvrSize > stringSize and break | |
yield string[ (curOffset - 16)...curOffset + 4 ] + string[curOffset + 4, pvrSize] | |
end | |
end | |
end | |
inputPath = Dir.pwd | |
outputRootDir = "EXTRACTS"; outputRootPath = "#{inputPath}/#{outputRootDir}" | |
outputPathPNGs = "#{outputRootPath}/PNGs" | |
outputPathPVRs = "#{outputRootPath}/PVRs" | |
# "If the output directories don't exist, then they're created [in the right order, from the outside to the inside]" | |
[outputRootPath, outputPathPNGs, outputPathPVRs].each { |path| Dir.exist_or_make(path) } | |
# "Start accessing the whole of the sub-tree." | |
Dir.glob("#{inputPath}/**/*") { |element| # "For each element found:" | |
element[0, outputRootPath.size] == outputRootPath and next # "if it's found in the root directory, it's ignored, else" | |
elementFilename = File.basename(element) | |
elementPath = File.dirname(element) | |
outputPathPNG = elementPath.sub(inputPath, outputPathPNGs); cmdOutputPathPNG = outputPathPNG.gsub("/", "\\") | |
outputPathPVR = elementPath.sub(inputPath, outputPathPVRs); cmdOutputPathPVR = outputPathPVR.gsub("/", "\\") | |
if File.directory?(element) # "if it's a directory:" | |
# "if they don't exist already, create in each of the output sub-directories a directory named as the element" | |
Dir.exist_or_make("#{outputPathPNG}/#{elementFilename}") | |
Dir.exist_or_make("#{outputPathPVR}/#{elementFilename}") | |
else # "if it's not a directory (and thus it's a file):" | |
element == File.basename(__FILE__) and next # "if it's the file __FILE__, it's ignored, else" | |
id = 0 | |
File.pvrDissect(element) { |data| # "dissect it and" | |
# "save the result in the matching (based on which directory it's found) directory of PVRs" | |
suffixPNG = "#{elementFilename}_\##{id}.PNG"; suffixPVR = "#{elementFilename}_\##{id}.PVR" | |
id += 1; IO.binwrite("#{outputPathPVR}/#{suffixPVR}", data) | |
%x{Pvr2Png.exe #{ "#{cmdOutputPathPVR}\\#{suffixPVR}" } #{ "#{cmdOutputPathPNG}\\#{suffixPNG}" } } | |
begin | |
outputPNG = "#{outputPathPNG}/#{suffixPNG}" | |
ChunkyPNG::Image.from_file(outputPNG).flip.save(outputPNG) | |
rescue | |
next | |
end | |
} | |
end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment