February 27, 2021, 03:45:45 AM

Author Topic: [bmx] RIFF Reader/Writer by spacerat [ 1+ years ago ]  (Read 572 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
[bmx] RIFF Reader/Writer by spacerat [ 1+ years ago ]
« on: June 29, 2017, 12:28:40 AM »
Title : RIFF Reader/Writer
Author : spacerat
Posted : 1+ years ago

Description : This module allows you to manipulate RIFF files in blitz. The RIFF file format is a container format, like XML, used by other formats, including WAVE and AVI. Like XML it is hierarchical, but unlike XML it is a binary format, not a text format, and everything is stored as efficiently as possible, so that the header for each chunk of data (including the file itself) consists only of 8 bytes, 4 for a tag, another 4 for the size of the chunk. List chunks, which contain other chunks, have a further 4 bytes for the list ID.

The module is OO, but there are a few non-OO functions for those who like them. Currently, when reading, the entire file is read into memory at once, so you can't stream with the module, and similarly files are simply overwritten when saving a RIFF file. These might be a problem if you're working with large files, but for a simple custom file format (for example, a tile map) the module is sufficient.


Code :
Code: BlitzMax
  1. SuperStrict
  2.  
  3. Rem
  4. bbdoc: RIFF File Manipulator
  5. about: The RIFF module allows easy read/write access to RIFF format files.
  6. End Rem
  7. Module JOE.RIFF
  8.  
  9.  
  10. ModuleInfo "Version: 0.9.0"
  11. ModuleInfo "Author: Joseph 'Spacerat' Atkins-Turkish"
  12. ModuleInfo "License: Public Domain (see source for notes)"
  13. ModuleInfo "History: 0.9.0 Release to blitzmax.com"
  14.  
  15. Rem
  16. This module is public domain because it's pretty simple, but if you use it for anything you release
  17. I would appreciate it if you send a link to your project to spacerat3004@gmail.com, just because I like to know (if) my stuff is being used.
  18. EndRem
  19.  
  20. Import Brl.Filesystem
  21. Import Brl.endianstream
  22. Import Brl.linkedlist
  23. Import brl.math
  24.  
  25.  
  26.  
  27. Rem
  28. bbdoc: Basic RIFF Chunk type
  29. about: The RIFF file structure is based on chunks, which all have a four character ID and contain binary data.
  30. End Rem
  31. Type RIFFChunk
  32.         Field ID:String
  33.         Field Data:TBank
  34.         Field Parent:RIFFList
  35.        
  36.         Function CreateChunk:RIFFChunk(ID:String, Size:Int = 0)
  37.                 Local n:RIFFChunk = New RIFFChunk
  38.                 n.ID = FourChrString(ID)
  39.                 n.Data = CreateBank()
  40.                 Return n
  41.         End Function
  42.        
  43.         Rem
  44.         bbdoc: Fill the chunk with data from a stream
  45.         EndRem
  46.         Method Fill(stream:TStream, datasize:Long)
  47.                 Data = CreateBank()
  48.                
  49.                 local bs:TBankStream = GetNewDataStream()
  50.                
  51.                 bs.WriteString(stream.ReadString(datasize))
  52.         End Method
  53.        
  54.         Rem
  55.         bbdoc: Get the size in bytes of the chunk's data
  56.         EndRem
  57.         Method GetSize:Long()
  58.                 Return Data.Size()
  59.         End Method
  60.        
  61.         Rem
  62.         bbdoc: Return a new stream for easy access to the chunk's data
  63.         EndRem
  64.         Method GetNewDataStream:TBankStream()
  65.                 Return TBankStream.Create(Data)
  66.         EndMethod
  67.        
  68.         Rem
  69.         bbdoc: Write the chunk to a stream
  70.         EndRem
  71.         Method Write(stream:TStream)
  72.                 stream.WriteString(FourChrString(ID))
  73.                 Local s:Long = GetSize()
  74.                 WriteUnsignedInt32(stream, s)
  75.                
  76.                 stream.WriteBytes(Data.Lock(), s)
  77.                 Data.Unlock()
  78.                
  79.                 'Extra null byte
  80.                 If GetSize() / 2.0 <> ceil(GetSize() / 2.0)
  81.                         stream.WriteByte(0)
  82.                 EndIf
  83.                
  84.         End Method
  85.        
  86.         Rem
  87.         bbdoc: Set the ID of the chunk
  88.         EndRem
  89.         Method SetName(fourchrs:String)
  90.                 ID = FourChrString(ID)
  91.         End Method
  92.        
  93.  
  94.        
  95. EndType
  96.  
  97. Rem
  98. bbdoc: RIFF List type
  99. about: The RIIF list is a special type of chunk which contains subchunks.
  100. EndRem
  101. Type RIFFList Extends RIFFChunk
  102.         Rem
  103.         bbdoc: The subchunk list.
  104.         about: This list contains all subchunks of the list in the order that they were read and will be written.
  105.         EndRem
  106.         Field Subchunks:TList = New TList
  107.         Field ChunkType:String
  108.        
  109.         Function CreateList:RIFFList(FormType:String)
  110.                 Local n:RIFFList = New RIFFList
  111.                 n.ID = "LIST"
  112.                 n.ChunkType = FourChrString(FormType)
  113.                 Return n
  114.         End Function
  115.                
  116.         Rem
  117.         bbdoc: Fill the chunk list with data from a stream
  118.         about: The stream data should consist of a a number of separate chunks in RIFF format.
  119.         EndRem
  120.         Method Fill(stream:TStream, datasize:Long)
  121.                 While datasize > 4
  122.                         Local nid:String = stream.ReadString(4)
  123.                         Local nsize:Long = ReadUnsignedInt32(stream)
  124.                        
  125.                         Local n:RIFFChunk
  126.                        
  127.                         If nid = "LIST"
  128.                                 n = New RIFFList
  129.                                 RIFFList(n).ChunkType = stream.ReadString(4)
  130.                                 n.ID = nid
  131.                         Else
  132.                                 n = New RIFFChunk
  133.                                 n.ID = nid
  134.                         EndIf
  135.                        
  136.                         n.ID = nid
  137.                         n.Fill(stream, nsize)
  138.                         AddChunk(n)
  139.                                                
  140.                         datasize:-(nsize + 8)
  141.                         If nsize / 2.0 <> ceil(nsize / 2.0)
  142.                                 datasize:-1
  143.                                 stream.ReadByte()
  144.                         EndIf
  145.                                                
  146.                 Wend
  147.         EndMethod
  148.         rem
  149.         bbdoc: Get the size of all chunks in the list, and the header
  150.         endrem
  151.         Method GetSize:Long()
  152.                 Local Size:Long
  153.                 For Local c:RIFFChunk = EachIn Subchunks
  154.                         Size = Size + c.GetSize() + 8
  155.                 Next
  156.                
  157.                 Return Size + 4
  158.         End Method
  159.        
  160.         Rem
  161.         bbdoc: Write the list to a stream
  162.         EndRem
  163.         Method Write(stream:TStream)
  164.                 stream.WriteString(FourChrString(ID))
  165.                 WriteUnsignedInt32(stream, GetSize())
  166.                 stream.WriteString(FourChrString(ChunkType))
  167.                
  168.                 For Local c:RIFFChunk = Eachin Subchunks
  169.                         c.Write(stream)
  170.                 Next
  171.                
  172.                 'Extra null byte
  173.                 If GetSize() / 2.0 <> ceil(GetSize() / 2.0)
  174.                         stream.WriteByte(0)
  175.                 EndIf
  176.                                
  177.         End Method
  178.        
  179.         Rem
  180.         bbdoc: Add a chunk to the end of the chunk list
  181.         EndRem
  182.         Method AddChunk(chunk:RIFFChunk)
  183.                 Subchunks.AddLast(chunk)
  184.                 chunk.Parent = Self
  185.         End Method
  186.        
  187.         Rem
  188.         bbdoc: Add a new chunk containing string data
  189.         returns: Returns the new chunk.
  190.         EndRem
  191.         Method AddTag:RIFFChunk(name:String, str:String)
  192.                 Local n:RIFFChunk = RIFFChunk.CreateChunk(name)
  193.                 AddChunk(n)
  194.                 n.GetNewDataStream().WriteString(str)
  195.                 Return n
  196.         End Method
  197.        
  198.         Rem
  199.         bbdoc: Add a new chunk to the list
  200.         EndRem
  201.         Method NewChunk:RIFFChunk(name:String)
  202.                 Local n:RIFFChunk = RIFFChunk.CreateChunk(name)
  203.                 AddChunk(n)
  204.                 Return n
  205.         End Method
  206.        
  207.        
  208.         Rem
  209.         bbdoc: Add a new list to the list
  210.         EndRem
  211.         Method NewList:RIFFList(format:String)
  212.                 Local n:RIFFList = RIFFList.CreateList(format)
  213.                 AddChunk(n)
  214.                 Return n
  215.         End Method
  216.                
  217.         Rem
  218.         bbdoc: Find a chunk by its name
  219.         about: Searches for and returns the first chunk found with an ID equal to @name,
  220.         or in the case of list chunks, a chunk type equal to @name.
  221.        
  222.         The search is not recursive, and will not search in subchunks of subchunks.
  223.         Endrem
  224.         Method FindChunk:RIFFChunk(name:String)
  225.                 name = FourChrString(name)
  226.                 For local c:RIFFChunk = EachIn Subchunks
  227.                        
  228.                         If c.ID = name Return c
  229.                         IF RIFFList(c)
  230.                                 if RIFFList(c).ChunkType = name Return c
  231.                         End If
  232.                        
  233.                 Next
  234.         End Method
  235.        
  236.         Rem
  237.         bbdoc: Set the list type of the list.
  238.         EndRem
  239.         Method SetName(fourchrs:String)
  240.                 ChunkType = FourChrString(ID)
  241.         End Method     
  242.        
  243.         Method GetNewDataStream:TBankStream()
  244.                 Throw "Cannot create data streams for list chunks."
  245.         End Method
  246.        
  247. EndType
  248. Rem
  249. bbdoc: The RIFF File type
  250. about: A RIFF file is essentially one large list chunk, with a number of subchunks/lists.
  251. It is entirely possible to construct, save and load RIFF files just using RIFFList, but
  252. this type exists for conveniance.
  253. EndRem
  254. Type RIFFFile Extends RIFFList
  255.        
  256.         Function CreateFile:RIFFFile(FileFormat:String, LittleEndian:Int = True)
  257.                 Local n:RIFFFile = New RIFFFile
  258.                 n.ChunkType = FileFormat
  259.                 If LittleEndian = 1
  260.                         n.ID = "RIFF"
  261.                 Else
  262.                         n.ID = "RIFX"
  263.                 End If
  264.                 Return n
  265.         EndFunction
  266.        
  267.         Rem
  268.         bbdoc: Load a RIFF file from a url
  269.         EndRem
  270.         Function Load:RIFFFile(url:Object)
  271.                 Local stream:TStream = OpenStream(url)
  272.                 If Not(stream) Return Null
  273.                
  274.                 Local n:RIFFFile = New RIFFFile
  275.                
  276.                 Local id:String = stream.ReadString(4)
  277.                 Select id
  278.                         Case "RIFF"
  279.                                 stream = LittleEndianStream(stream)
  280.                         Case "RIFX"
  281.                                 stream = BigEndianStream(stream)
  282.                         Default
  283.                                 Return Null
  284.                 End Select             
  285.                
  286.                 n.ID = id
  287.                 Local nsize:Long = ReadUnsignedInt32(stream)
  288.                 n.ChunkType = stream.ReadString(4)
  289.                
  290.                 n.Fill(stream, nsize)
  291.                
  292.                 Return n
  293.         End Function
  294.        
  295.         Rem
  296.         bbdoc: Save the file to a stream or URL
  297.         about: Files are not automatically created.
  298.         EndRem
  299.         Method Save(url:Object)
  300.                 Local s:TStream = OpenStream(url)
  301.                 If s = Null
  302.                 '       Print "Save error: null stream"
  303.                         Return
  304.                 End If
  305.                 Write(s)
  306.         End Method
  307.  
  308. End Type
  309.  
  310. Rem
  311. bbdoc: Create and initialise a new RIFF chunk
  312. about: The @ID should be four characters, and is trimmed or padded otherwise.
  313. EndRem
  314. Function CreateRIFFChunk:RIFFChunk(ID:String, parent:RIFFList = Null)
  315.         Local c:RIFFChunk = RIFFChunk.CreateChunk(ID, 0)
  316.         If (parent) parent.AddChunk(c)
  317.         Return c
  318. EndFunction
  319.  
  320. Rem
  321. bbdoc: Create and initialise a new RIFF LIST
  322. about: The @FormType should be four characters, and is trimmed or padded otherwise.
  323. EndRem
  324. Function CreateRIFFList:RIFFList(FormType:String, parent:RIFFList = Null)
  325.         Local c:RIFFList = RIFFList.CreateList(FormType)
  326.         If (parent) parent.AddChunk(c)
  327.         Return c
  328. Endfunction
  329.  
  330. Rem
  331. bbdoc: Create and initialise a new RIFF File
  332. about: The @FileFormat should be four characters, and is trimmed or padded otherwise.
  333.  
  334. If @LittleEndian is true the file is a RIFF file, else it is a RIFX file.
  335. EndRem
  336. Function CreateRIFFFile:RIFFFile(FileFormat:String, LittleEndian:Int = True)
  337.         Return RIFFFile.CreateFile(FileFormat, LittleEndian)
  338. EndFunction
  339.  
  340. Rem
  341. bbdoc: Load a RIFF file from a url
  342. EndRem
  343. Function LoadRIFFFile:RIFFFile(url:Object)
  344.         Return RIFFFile.Load(url)
  345. End Function
  346.  
  347. Rem
  348. Some extra utility functions required for the module. You also might find them useful, espectially read/write unsignedInt32.
  349. Also please let me know if you can think of a better way of doing it that doesn't involve longs.
  350. EndRem
  351.  
  352. Function FourChrString:String(str:String)
  353.         If str.Length = 4 Return str
  354.         If str.Length > 4
  355.                 Return str[0..4]
  356.         End If
  357.        
  358.         While str.Length < 4
  359.                 str = str + " "
  360.         WEnd
  361.         Return str
  362. End Function
  363.  
  364. Function WriteUnsignedInt32(stream:TStream, l:Long)
  365.         Local p:Int Ptr = Int Ptr VarPtr l
  366.        
  367.         ?LittleEndian
  368.         stream.WriteInt(p[0])
  369.         ?BigEndian
  370.         stream.WriteInt(p[1])
  371.         ?
  372.  
  373. End Function
  374.  
  375. Function ReadUnsignedInt32:Long(stream:TStream)
  376.         Local i:Int = stream.ReadInt()
  377.         Local l:Long
  378.         Local p:Int Ptr = Int Ptr VarPtr l
  379.         ?LittleEndian
  380.         p[0] = i
  381.         ?BigEndian
  382.         p[1] = i
  383.         ?
  384.         Return l
  385. End Function


Comments : none...

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal