Oddworld Forums > Zulag One > Oddworld Discussion > Oddworld Mods & Hacks


 
Thread Tools
 
  #1  
12-21-2013, 12:45 PM
m-35's Avatar
m-35
Zappfly
 
: Dec 2013
: At home
: 1
Rep Power: 0
m-35  (10)
PSX .CAM file

Noticed some discussion here and more recently here that .CAM files contain PlayStation MDEC data. Did some poking around and managed to get the images out. Here's a quick Python script (uses jPSXdec for the heavy lifting).

:
"""
Extract PlayStation MDEC bitstreams from an Oddworld PlayStation .CAM file
(found in .LVL files) and converts those bitstreams to images with jPSXdec.
The resulting images will need to be stitched together for form a full frame.
Note that there is other data in .CAM files that this tool ignores.

Usage:
python cam2bs.py cam-file
"""

import sys, os, struct

# Customize the jPSXdec command with quality and output options (see manual)
JPSXDEC_COMMAND = "java -jar jpsxdec.jar -f {0} -static bs -dim {1}x{2}"

class CamFramePiece:
    '''
    One piece of a .CAM file frame.
    All pieces are 240 pixels high, and all but the
    last piece is 32 pixels wide.
    The last piece is 16 pixels wide.
    The piece format is simple:
        4 bytes (little-endian): size of the bitstream data
        bitstream data
    '''

    def __init__(self, iPiece, fileStream):
        self.iPiece = iPiece
        self.iFilePos = fileStream.tell()
        sbinPieceLen = fileStream.read(4)
        self.iPieceLen, = struct.unpack('i', sbinPieceLen)
        if self.iPieceLen <= 0:
            raise Exception('BAD: %s frame len <= 0' % self)
        self.bitstreamBytes = fileStream.read(self.iPieceLen)
        iMarker00, iMarker38 = struct.unpack('xxBB', self.bitstreamBytes[0:4])
        if iMarker00 != 0x00 or iMarker38 != 0x38:
            raise Exception('BAD: %s missing 0x0038' % self)

    def savePiece(self, sSrcFile):
        if self.iPiece == 11:
            self.iWidth = 16
        else:
            self.iWidth = 32
        self.sFileName = '%s_%02d_%dx%d.bs' % (sSrcFile, self.iPiece, self.iWidth, 240)
        print 'Saving %s piece %d as %s' % (sSrcFile, self.iPiece, self.sFileName)
        with open(self.sFileName, 'wb') as f:
            f.write(self.bitstreamBytes)

    def decodePiece(self):
        print 'Converting %s to image with jPSXdec' % self.sFileName
        sCmd = JPSXDEC_COMMAND.format(self.sFileName, self.iWidth, 240)
        print sCmd
        os.system(sCmd)

    def __str__(self):
        return '#%d @%d: Len=%d' % (self.iPiece, self.iFilePos, self.iPieceLen)
    def __repr__(self):
        return self.__str__()

class CamFile:
    '''
    Oddworld .CAM file.
    Contains 12 separate images (CamFramePiece).
    When combined together, forms a 368x240 frame.
    The header is unknown, and there are quite a few bytes at the end of the file that are unknown.
        8 bytes: unknown
        4 bytes (big-endian): string "Bits"
        4 bytes: unknown
        body (variable size): 12 frame pieces (see CamFramePiece)
        remaining (variable size): unknown
    '''
    def __init__(self, sFile):
        print 'Extracting bitstream pieces from %s' % sFile
        with open(sFile, 'rb') as fileStream:
            self.sSrcFile = sFile
            self.lstPieces = []
            fileStream.read(8) # unknown 8 bytes
            sBits = fileStream.read(4)
            if sBits != 'Bits':
                raise Exception(sBits+' != Bits')
            fileStream.read(4) # unknown 4 bytes
            for iPiece in xrange(12):
                self.lstPieces.append(CamFramePiece(iPiece, fileStream))

    def saveBitstreams(self):
        for piece in self.lstPieces:
            piece.savePiece(self.sSrcFile)

    def decodeBitstreams(self):
        for piece in self.lstPieces:
            piece.decodePiece()
            print

    def printInfo(self):
        for piece in self.lstPieces:
            print piece
            

def main(lstArgs):
    camFile = CamFile(lstArgs[0])
    camFile.printInfo()
    print
    camFile.saveBitstreams()
    print
    camFile.decodeBitstreams()
    return 0

if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
Reply With Quote
  #2  
12-21-2013, 01:45 PM
Paul's Avatar
Paul
Outlaw Sniper
 
: Jun 2007
: MilkyWay
: 1,535
Rep Power: 18
Paul  (718)Paul  (718)Paul  (718)Paul  (718)Paul  (718)Paul  (718)Paul  (718)

Good work, you should find my lvl extractor command line tool and update your script to extract and convert all cams in a lvl, put them together and save as png, that would be nice

I didn't realize it had a command line option to pass a bitstream, I figured you'd have to hack the source to get it to work. I'm glad my responses actually mean something to someone too, most people are just like "huh?" and carry on.

Edit: What happens if you try this on a PC AE cam? Those are 640x240.
__________________
[ http://www.paulsapps.com ]

Crawling sligs will shout "Mommy!" while running around and then the slig mommy will appear and help them put their pants on.

Reply With Quote
  #3  
12-21-2013, 10:58 PM
Weezenhimer's Avatar
Weezenhimer
Chippunk
 
: Jan 2012
: Africa
: 22
Rep Power: 0
Weezenhimer  (17)

:
Good work, you should find my lvl extractor command line tool and update your script to extract and convert all cams in a lvl, put them together and save as png, that would be nice

I didn't realize it had a command line option to pass a bitstream, I figured you'd have to hack the source to get it to work. I'm glad my responses actually mean something to someone too, most people are just like "huh?" and carry on.

Edit: What happens if you try this on a PC AE cam? Those are 640x240.
I wonder if(almost) the same thing m-35 did was done on my Russian copies of AO (translation done by "Kudos",as the copyright is replaced by a white message on a black screen,and the menus are edited as well,will post a pic later)and AE (same edits,though this time the audio's edited as well,instead of hello it says "privet!")since both of those have the copyright screen edited.
Here's a screenshot.Instead of the copyright screen (the one with (C)Oddworld Inhabitants etc.)the copy of russian AE I have shows a russian text followed by the Kudos logo.Sorry it's in B/W,I used a older PS1 (5552)that doesn't have the color mod done.(the logo you see in the picture should be in color and I think it's colored blue/gold)


Also,Paul,the LCD text in both AE and AO can be edited with OgreGUI.I used it and successfully translated LCD boards in Feeco Depot.
Attached Thumbnails
Click image for larger version

Name:	changed screen.png
Views:	1010
Size:	92.8 
ID:	13920  
__________________


Last edited by Weezenhimer; 12-21-2013 at 11:02 PM..
Reply With Quote
  #4  
01-12-2014, 10:39 AM
Paul's Avatar
Paul
Outlaw Sniper
 
: Jun 2007
: MilkyWay
: 1,535
Rep Power: 18
Paul  (718)Paul  (718)Paul  (718)Paul  (718)Paul  (718)Paul  (718)Paul  (718)

It would be cool to see all of the demos cams get dumped somewhere to see if there are any hidden beta screens
__________________
[ http://www.paulsapps.com ]

Crawling sligs will shout "Mommy!" while running around and then the slig mommy will appear and help them put their pants on.

Reply With Quote


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 








 
 
- Oddworld Forums - -