UnderwareDESIGN

PlayBASIC => Resources => Source Codes => Topic started by: kevin on March 12, 2013, 08:46:07 AM

Title: SPR File Decoder / Loader
Post by: kevin on March 12, 2013, 08:46:07 AM
 SPR File Decoder / Loader

   This example decodes null byte compressed palette mapped sprites from constructor into a series of regular RGB images.  The convertor doesn't include a palette decoder, since that data isn't stored within the sprite file,  it's housed else where.  But the format is just IFF so loading them is trivial, I just don't have the required file, so haven't bothered.

  Info on the sprite format can be found here
  https://github.com/shlainn/game-file-formats/wiki/Constructor-.SPR-files



[pbcode]

   ; File to decode
   file$="Location_Of_File_Here.spr"

   // ---------------------------------------------------------
   // Here we're building a shaded palette in whatever colour we want
   // since sprite files don't actually contain the palette data in them
   // the palette is stored in a different file.
   
   //  Link to a SPR format dizzy
   //  https://github.com/shlainn/game-file-formats/wiki/Constructor-.SPR-files
   // ---------------------------------------------------------
   
   Dim Palette(255)
   For lp =0 to 255
         Palette(lp)=rgbalphamult(rgb(lp,lp,lp),$ffffff)
   next   

   // Load the Sprite file into a bank
   Bank=LoadFileToBank(file$)


   // DIm the array that will hold the converted frames
   Dim Sprites(0)

   // Decode the raw frames
   Sprites()=Decode_Sprites(Bank,Palette())

   // The bank is no longer needed so delete it
   deleteBank Bank



   

; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
; -[ MAIN LOOP ]-------------------------------------------------------
; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
   
   SetFps 60
   
   Frame=1   

   Maxframes=GetArrayElements(Sprites())

   Do   

      ;clear screen to black
      Cls

      ; check for up arrow
      if upkey() and Frame>1
         Frame--
      endif

      ; check for down arrow
      if downkey() and Frame<MaxFrames
         Frame++
      endif

      ; get the image of this frame
      ThisImage=Sprites(Frame)

      if ThisIMage         
         Width#=GetImageWidth(ThisImage)
         Height#=GetImageWidth(ThisImage)
         drawRotatedimage  ThisIMage,400,300,0,2,2,Width#/-2,Height#/-2,false
      endif

      print "Sprite frame#"+str$(frame)+" of #"+str$(MaxFrames)
   
      sync
   loop


; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
; -[ Decode Sprites ]-------------------------------------------------------
; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------

Function Decode_Sprites(SpriteBank,Palette())

   Dim Frames(0)

   if GetBankStatus(SpriteBank)
   
      SpriteBankSize=GetBankSize(SpriteBank)
   
      if SpriteBankSize>4

         NumberOfFrames = PeekBankInt(SpriteBank, 0 ) ; read integer from mem block at offset 0

         DIm Frames(NumberOfFrames)

         print NumberOfFrames

   
          // -------------------------------------   
          // decode frames
          // -------------------------------------   
            For framelp=0 to NumberOfFrames-1

             // Get the offset with the mem block of where this frame starts
             FrameDataOffset = PeekBankInt(SpriteBank, 4+(FrameLp*4) )

            // does this frame have any data ?
            if FrameDataOffset<>0
                     #print FrameDataOffset


                  // --------------------------------------------------------------------------------
                 // decompress frame, here i'm assuming the frame offset is relative to the start
                 // of the file, it might be relative to it's position it the sprite table  
                    // --------------------------------------------------------------------------------

                 // Grab the info about the sprite from it's header.
      
                 FrameWidth=PeekBankWORD(SpriteBank, FrameDataOffset + 4 )
                 FrameHeight=PeekBankWORD(SpriteBank, FrameDataOffset + 6 )

                  // calc current /start offset of the compressed pixel data for this frame
                 PixelDataAddress =FrameDataOffset + 8

                  // Alloc a bank to store the temp pixel data.
                  // it's probably a good idea to pad this, just in case :)
 
                   DecompressedBank = NewBank(  (FrameWidth+1) * FrameHeight)    

                  #print FrameWidth
                  #print FrameHeight
                  #print DecompressedBank
                  #print ""
                  

                 // --------------------------------------------------------------------------------
                  // decode the pixel data that makes up to this frame into the temp bank
                  // --------------------------------------------------------------------------------

                 MaxPixelCount = FrameWidth * FrameHeight
                  PixelCount =0


                  repeat
                      // read the pixel
                     ThisPixel =  PeekBankByte(SpriteBank, PixelDataAddress)
              
                     // is pixel zero ??
                     if ThisPixel=0

                        // decode run length fragment (restore run of zeros)
                        RunSize =  PeekBankByte(SpriteBank, PixelDataAddress+1)
                       For WriteLP=1 to RunSize
                            POkeBankByte DecompressedBank,PixelCount,ThisPixel
                              Inc PixelCount
                       next

                        // step src offset/pointer forward
                       PixelDataAddress=PixelDataAddress+2

                      else

                        // write the pixel to output bank  
                        POkeBankByte DecompressedBank,PixelCount,ThisPixel
                        inc PixelDataAddress
                       Inc PixelCount
                  endif
 
                 // check if we're reached the end of sprite
                 until PixelCount=>MaxPixelCount



               ThisImage=NewIMage(FrameWidth,FrameHeight,2)
            
               rendertoimage ThisIMage
              PixelCount =0
             
             lockbuffer
                    ThisPixel=point(0,0)
                  For ylp=0 to FrameHeight-1
                    For xlp=0 to FrameWidth-1
                        // Get this pixel index
                       ThisPixel=PeekBankByte(DecompressedBank,PixelCount)
                       // Convert the palette map index into the output RGB colour & draw this dot
                       Fastdot Xlp,ylp,Palette(ThisPixel)
                     PixelCount++
                    next
                 next
                unlockbuffer
                  Frames(framelp)=ThisImage
                  deletebank DecompressedBank
               
            endif



            rendertoscreen
            if Frames(framelp)
               cls 0
               print "Decoding Frame#"+str$(framelp)
               sync
            endif
   
         next
      endif
   
   endif

EndFunction Frames()


; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
; -[ Load File to Bank ]-------------------------------------------------------
; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------

Function LoadFileToBank(file$)
   if Fileexist(file$)
      size=filesize(File$)
      ThisBank=NewBank(Size)
      fh=ReadnewFile(File$)
      if fh
            ReadMemory fh,GetBankPtr(ThisBank),Size
         closefile fh
      endif
   endif
EndFunction ThisBank


[/pbcode]


Related Articles:

    * C64 Styled Sprite Rendering Example (http://www.underwaredesign.com/forums/index.php?topic=4022.0)
    * Palette Mapped / Raster Bar Examples (http://www.underwaredesign.com/forums/index.php?topic=4060.0)
    * LowRES PlayBASIC - 8bit Retro Graphics Library (https://www.underwaredesign.com/forums/index.php?topic=4725.0)
     * Challenge #27 -  Learn binary operations through Retro Computer Graphics (http://www.underwaredesign.com/forums/index.php?topic=4147.0)