738 lines
16 KiB
C++
738 lines
16 KiB
C++
/*
|
|
** nsvlib.cpp - NSV file/bitstream reading/writing code
|
|
**
|
|
** Copyright (C) 2001-2002 Nullsoft, Inc.
|
|
**
|
|
** Confidential Subject to NDA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <bfc/platform/strcmp.h>
|
|
|
|
#include "nsvlib.h"
|
|
|
|
#define NSV_HDR_DWORD (NSV_MAKETYPE('N','S','V','f'))
|
|
|
|
#define NSV_SYNC_HEADERLEN_BITS 192
|
|
#define NSV_SYNC_DWORD (NSV_MAKETYPE('N','S','V','s'))
|
|
#define NSV_NONSYNC_HEADERLEN_BITS 56
|
|
#define NSV_NONSYNC_WORD 0xBEEF
|
|
|
|
#define NSV_INVALID_SYNC_OFFSET 0x80000000
|
|
|
|
long glSyncFrameCount = 0l;
|
|
long glCounterNSVf = 0l;
|
|
long glNonSyncFrameCount = 0l;
|
|
|
|
/*
|
|
NSV sync packet header
|
|
32 bits: NSV_SYNC_DWORD
|
|
32 bits: video format
|
|
32 bits: audio format
|
|
16 bits: width
|
|
16 bits: height
|
|
8 bits: framerate (see getfrate/setfrate)
|
|
|
|
|
|
16 bits: audio/video sync offset
|
|
|
|
or
|
|
|
|
NSV nonsync packet header
|
|
16 bits: NSV_NONSYNC_WORD
|
|
|
|
then
|
|
|
|
4 bits: # aux data channels present (max 15)
|
|
20 bits: video data + aux channels length
|
|
16 bits: audio data length
|
|
|
|
--------------------------------
|
|
sync:
|
|
192 bit header,
|
|
136 bits are invariant
|
|
nonsync:
|
|
56 bit header
|
|
16 bits are invariant
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int is_type_char_valid(int c)
|
|
{
|
|
c&=0xff;
|
|
return (c >= 'a' && c <= 'z') ||
|
|
(c >= 'A' && c <= 'Z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
c == ' ' || c == '-' ||
|
|
c == '.' || c == '_';
|
|
}
|
|
|
|
static int is_type_valid(unsigned int t)
|
|
{
|
|
return (t&0xff) != ' ' &&
|
|
is_type_char_valid(t>>24) &&
|
|
is_type_char_valid(t>>16) &&
|
|
is_type_char_valid(t>>8) &&
|
|
is_type_char_valid(t);
|
|
}
|
|
|
|
|
|
void nsv_type_to_string(unsigned int t, char *out)
|
|
{
|
|
if (is_type_valid(t))
|
|
{
|
|
out[0]=(t)&0xff;
|
|
out[1]=(t>>8)&0xff;
|
|
out[2]=(t>>16)&0xff;
|
|
out[3]=(t>>24)&0xff;
|
|
out[4]=0;
|
|
int x=3;
|
|
while (out[x]==' ' && x > 0) out[x--]=0;
|
|
}
|
|
else *out=0;
|
|
}
|
|
|
|
unsigned int nsv_string_to_type(char *in)
|
|
{
|
|
int n;
|
|
unsigned int ret=*in;
|
|
if (*in == ' ' || !is_type_char_valid(*in)) return 0;
|
|
in++;
|
|
for (n = 0; n < 3; n ++)
|
|
{
|
|
if (!is_type_char_valid(*in)) break;
|
|
ret|=(*in<<(8+8*n));
|
|
in++;
|
|
}
|
|
if (*in) return 0;
|
|
return ret;
|
|
}
|
|
|
|
// frate is specified
|
|
// XYYYYYZZ
|
|
// if !X, framerate is YYYYYZZ (1-127)
|
|
// otherwise:
|
|
// ZZ indexes base
|
|
// YYYYY is scale (0-32).
|
|
// if YYYYY < 16, then scale = 1/(YYYY+1)
|
|
// otherwise scale = YYYYY-15
|
|
|
|
|
|
static double frate2double(unsigned char fr)
|
|
{
|
|
static double fratetab[]=
|
|
{
|
|
30.0,
|
|
30.0*1000.0/1001.0,
|
|
25.0,
|
|
24.0*1000.0/1001.0,
|
|
};
|
|
if (!(fr&0x80)) return (double)fr;
|
|
|
|
double sc;
|
|
int d=(fr&0x7f)>>2;
|
|
if (d < 16) sc=1.0/(double)(d+1);
|
|
else sc=d-15;
|
|
|
|
return fratetab[fr&3]*sc;
|
|
}
|
|
|
|
|
|
|
|
static unsigned char double2frate(double fr)
|
|
{
|
|
int best=0;
|
|
double best_v=1000000.0;
|
|
int x;
|
|
for (x = 0; x < 256; x ++)
|
|
{
|
|
double this_v=(fr-frate2double(x));
|
|
|
|
if (this_v<0) this_v=-this_v;
|
|
if (this_v < best_v)
|
|
{
|
|
best_v=this_v;
|
|
best=x;
|
|
}
|
|
}
|
|
return (unsigned char) best;
|
|
}
|
|
|
|
|
|
|
|
nsv_Packeter::nsv_Packeter()
|
|
{
|
|
vidfmt=audfmt=0;
|
|
width=height=0;
|
|
framerate_idx=0;
|
|
framerate=0.0;
|
|
syncoffset_cur=0;
|
|
video=NULL;
|
|
audio=NULL;
|
|
video_len=0;
|
|
audio_len=0;
|
|
aux_used=0;
|
|
}
|
|
|
|
void nsv_Packeter::setVidFmt(unsigned int vfmt, unsigned int w, unsigned int h, double frt)
|
|
{
|
|
vidfmt=vfmt;
|
|
width=w;
|
|
height=h;
|
|
framerate=frt;
|
|
framerate_idx=double2frate(frt);
|
|
}
|
|
|
|
nsv_Packeter::~nsv_Packeter()
|
|
{
|
|
}
|
|
|
|
int nsv_Packeter::packet(nsv_OutBS &bs)
|
|
{
|
|
int total_auxlen=0;
|
|
int x;
|
|
if (width >= (1<<16) || height >= (1<<16) ||
|
|
!framerate_idx || framerate_idx > 255 ||
|
|
!is_type_valid(audfmt) ||
|
|
!is_type_valid(vidfmt) ||
|
|
video_len > NSV_MAX_VIDEO_LEN ||
|
|
audio_len > NSV_MAX_AUDIO_LEN ||
|
|
aux_used > NSV_MAX_AUXSTREAMS ||
|
|
aux_used < 0
|
|
) return -1;
|
|
|
|
for (x = 0; x < aux_used; x ++)
|
|
{
|
|
if (aux_len[x] > NSV_MAX_AUX_LEN) return -1;
|
|
total_auxlen+=aux_len[x]+6;
|
|
}
|
|
|
|
if (is_sync_frame)
|
|
{
|
|
bs.putbits(32,NSV_SYNC_DWORD);
|
|
bs.putbits(32,vidfmt);
|
|
bs.putbits(32,audfmt);
|
|
bs.putbits(16,width);
|
|
bs.putbits(16,height);
|
|
bs.putbits(8 ,framerate_idx);
|
|
bs.putbits(16,syncoffset_cur);
|
|
}
|
|
else
|
|
{
|
|
bs.putbits(16,NSV_NONSYNC_WORD);
|
|
}
|
|
|
|
bs.putbits(4,aux_used); // no aux data channels for our streams yet
|
|
bs.putbits(20,video_len+total_auxlen);
|
|
bs.putbits(16,audio_len);
|
|
|
|
for (x = 0; x < aux_used; x ++)
|
|
{
|
|
bs.putbits(16,aux_len[x]); // length of 0 for aux channels
|
|
bs.putbits(32,aux_types[x]);
|
|
if (aux_len[x]) bs.putdata(aux_len[x]*8,aux[x]);
|
|
}
|
|
|
|
if (video_len) bs.putdata(video_len*8,video);
|
|
if (audio_len) bs.putdata(audio_len*8,audio);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void nsv_Unpacketer::reset(int full)
|
|
{
|
|
synched=0;
|
|
is_sync_frame=0;
|
|
syncoffset_cur=0;
|
|
syncoffset=NSV_INVALID_SYNC_OFFSET;
|
|
|
|
if (full)
|
|
{
|
|
m_auxbs=NULL;
|
|
m_audiobs=NULL;
|
|
m_videobs=NULL;
|
|
m_eof=0;
|
|
vidfmt=0;
|
|
audfmt=0;
|
|
valid=0;
|
|
width=0;
|
|
height=0;
|
|
framerate=0.0;
|
|
framerate_idx=0;
|
|
}
|
|
}
|
|
|
|
|
|
// returns 0 on success, >0 on needs (at least X bytes) more data,
|
|
// -1 on error (no header found in block)
|
|
int nsv_Unpacketer::unpacket(nsv_InBS &bs)
|
|
{
|
|
int gotframe=0;
|
|
unsigned int num_aux=0;
|
|
unsigned int vl=0;
|
|
unsigned int al=0;
|
|
|
|
while (bs.avail()>=NSV_NONSYNC_HEADERLEN_BITS)
|
|
{
|
|
if (valid && synched)
|
|
{
|
|
if (bs.avail() < NSV_NONSYNC_HEADERLEN_BITS)
|
|
return m_eof?-1:(NSV_NONSYNC_HEADERLEN_BITS- (int)(bs.avail())/8);
|
|
|
|
unsigned int d=bs.getbits(16);
|
|
if (d == NSV_NONSYNC_WORD)
|
|
{
|
|
glNonSyncFrameCount++;
|
|
num_aux=bs.getbits(4);
|
|
vl=bs.getbits(20);
|
|
al=bs.getbits(16);
|
|
if (al >= NSV_MAX_AUDIO_LEN ||
|
|
vl >= (NSV_MAX_VIDEO_LEN+num_aux*(NSV_MAX_AUX_LEN+6)))
|
|
{
|
|
bs.seek(-NSV_NONSYNC_HEADERLEN_BITS);
|
|
}
|
|
else
|
|
{
|
|
if ((unsigned int)bs.avail() < 8*(vl+al)+(m_eof?0:32))
|
|
{
|
|
int l=(al+vl+32/8)- (int)(bs.avail()/8);
|
|
bs.seek(-NSV_NONSYNC_HEADERLEN_BITS);
|
|
return m_eof?-1:l;
|
|
}
|
|
|
|
if ((unsigned int)bs.avail() >= 8*(vl+al)+32)
|
|
{
|
|
bs.seek(8*(vl+al));
|
|
unsigned int a32=bs.getbits(32);
|
|
bs.seek(-32);
|
|
unsigned int a16=bs.getbits(16);
|
|
bs.seek(-16);
|
|
bs.seek(-8*(vl+al));
|
|
if (a16 != NSV_NONSYNC_WORD && a32 != NSV_SYNC_DWORD)
|
|
{
|
|
bs.seek(-NSV_NONSYNC_HEADERLEN_BITS);
|
|
}
|
|
else gotframe=NSV_NONSYNC_HEADERLEN_BITS;
|
|
}
|
|
else gotframe=NSV_NONSYNC_HEADERLEN_BITS;
|
|
}
|
|
}
|
|
else bs.seek(-16);
|
|
} // inf.valid && inf.synched
|
|
|
|
// gotframe is set if we successfully got a nonsync frame, otherwise
|
|
// let's see if we can't interpret this as a sync frame
|
|
|
|
if (!gotframe)
|
|
{
|
|
if (bs.avail() < NSV_SYNC_HEADERLEN_BITS)
|
|
return (int)(m_eof?-1:(NSV_SYNC_HEADERLEN_BITS-(bs.avail())/8));
|
|
unsigned int d=bs.getbits(32);
|
|
if (d != NSV_SYNC_DWORD)
|
|
{
|
|
bs.seek(8-32); // seek back 3 bytes
|
|
synched=0;
|
|
continue;
|
|
}else{
|
|
// count the # of sync frames (for debugging)
|
|
glSyncFrameCount++;
|
|
}
|
|
unsigned int vfmt=bs.getbits(32);
|
|
unsigned int afmt=bs.getbits(32);
|
|
unsigned int w=bs.getbits(16);
|
|
unsigned int h=bs.getbits(16);
|
|
unsigned char frt=bs.getbits(8);
|
|
unsigned int so=bs.getbits(16);
|
|
|
|
num_aux=bs.getbits(4);
|
|
vl=bs.getbits(20);
|
|
al=bs.getbits(16);
|
|
|
|
if (al >= NSV_MAX_AUDIO_LEN ||
|
|
vl >= (NSV_MAX_VIDEO_LEN+num_aux*(NSV_MAX_AUX_LEN+6)) ||
|
|
!frt || !is_type_valid(vfmt) || !is_type_valid(afmt) ||
|
|
(valid &&
|
|
(width != w || height != h ||
|
|
vidfmt != vfmt || audfmt != afmt || framerate_idx != frt)))
|
|
{ // frame is definately not valid
|
|
bs.seek(8-NSV_SYNC_HEADERLEN_BITS); // seek back what we just read
|
|
synched=0;
|
|
continue;
|
|
}
|
|
|
|
if ((unsigned int)bs.avail() < (al+vl)*8+((m_eof||(valid&&synched))?0:32))
|
|
{
|
|
int l=(al+vl)*8+NSV_SYNC_HEADERLEN_BITS- (int)(bs.avail());
|
|
bs.seek(-NSV_SYNC_HEADERLEN_BITS);
|
|
return m_eof?-1:(l/8);
|
|
}
|
|
|
|
if (valid && synched)
|
|
{
|
|
gotframe=NSV_SYNC_HEADERLEN_BITS;
|
|
}
|
|
else // we need to do more robust sync
|
|
{
|
|
int sk=(al+vl)*8;
|
|
bs.seek(sk);
|
|
unsigned int a16=bs.getbits(16);
|
|
bs.seek(-16);
|
|
unsigned int a32=bs.getbits(32);
|
|
bs.seek(-32);
|
|
if (a16 == NSV_NONSYNC_WORD)
|
|
{
|
|
sk+=16+4+20+16;
|
|
bs.seek(16);
|
|
unsigned int _num_aux=bs.getbits(4);
|
|
unsigned int _vl=bs.getbits(20);
|
|
unsigned int _al=bs.getbits(16);
|
|
if ((unsigned int)bs.avail() < (_vl+_al)*8 + 32)
|
|
{
|
|
int l=(_al+_vl+32)- (int)(bs.avail()/8);
|
|
bs.seek(-NSV_SYNC_HEADERLEN_BITS-sk);
|
|
return m_eof?-1:l;
|
|
}
|
|
bs.seek((_vl+_al)*8);
|
|
sk+=(_vl+_al)*8;
|
|
unsigned int a16=bs.getbits(16);
|
|
bs.seek(-16);
|
|
unsigned int a32=bs.getbits(32);
|
|
bs.seek(-32);
|
|
bs.seek(-sk);
|
|
if (a16 == NSV_NONSYNC_WORD || a32 == NSV_SYNC_DWORD)
|
|
gotframe=NSV_SYNC_HEADERLEN_BITS;
|
|
}
|
|
else if (a32 == NSV_SYNC_DWORD)
|
|
{
|
|
glSyncFrameCount++;
|
|
|
|
sk+=32+32+32+16+16+8;
|
|
|
|
bs.seek(32);
|
|
unsigned int _vfmt=bs.getbits(32);
|
|
unsigned int _afmt=bs.getbits(32);
|
|
unsigned int _w=bs.getbits(16);
|
|
unsigned int _h=bs.getbits(16);
|
|
unsigned char _frt=bs.getbits(8);
|
|
bs.seek(-sk);
|
|
|
|
if (_vfmt==vfmt && _afmt==afmt && _w==w && _h==h && _frt==frt) // matches
|
|
{
|
|
gotframe=NSV_SYNC_HEADERLEN_BITS;
|
|
}
|
|
}
|
|
}
|
|
if (!gotframe)
|
|
{
|
|
synched=0;
|
|
bs.seek(8-NSV_SYNC_HEADERLEN_BITS);
|
|
}
|
|
else
|
|
{
|
|
if (so & 0x8000) so|=0xFFFF0000;
|
|
syncoffset_cur=so;
|
|
if (!valid || syncoffset == NSV_INVALID_SYNC_OFFSET) syncoffset=so;
|
|
if (!valid) framerate=frate2double(frt);
|
|
framerate_idx=frt;
|
|
width=w;
|
|
height=h;
|
|
audfmt=afmt;
|
|
vidfmt=vfmt;
|
|
valid=1;
|
|
synched=1;
|
|
}
|
|
}
|
|
|
|
if (gotframe)
|
|
{
|
|
is_sync_frame = (gotframe == NSV_SYNC_HEADERLEN_BITS);
|
|
// read aux channels
|
|
int rd=gotframe;
|
|
unsigned int x;
|
|
for (x = 0; x < num_aux; x ++)
|
|
{
|
|
unsigned int l=bs.getbits(16);
|
|
unsigned int fmt=bs.getbits(32);
|
|
vl -= 4+2;
|
|
rd += 16+32;
|
|
|
|
if (l > NSV_MAX_AUX_LEN) break;
|
|
|
|
if (m_auxbs)
|
|
{
|
|
m_auxbs->addint(l);
|
|
m_auxbs->addint(fmt);
|
|
m_auxbs->add(bs.getcurbyteptr(),l);
|
|
}
|
|
bs.seek(l*8); // toss aux
|
|
|
|
vl-=l;
|
|
rd+=l*8;
|
|
|
|
if (vl<0) break; // invalid frame (aux channels add up to more than video)
|
|
}
|
|
if (x < num_aux) // oh shit, invalid frame
|
|
{
|
|
synched=0;
|
|
bs.seek(8-rd);
|
|
gotframe=0;
|
|
continue;
|
|
}
|
|
|
|
if (m_videobs)
|
|
{
|
|
m_videobs->addint(vl);
|
|
m_videobs->add(bs.getcurbyteptr(),vl);
|
|
}
|
|
bs.seek(vl*8);
|
|
|
|
if (m_audiobs)
|
|
{
|
|
m_audiobs->addint(al);
|
|
m_audiobs->add(bs.getcurbyteptr(),al);
|
|
}
|
|
bs.seek(al*8);
|
|
|
|
return 0;
|
|
}
|
|
} // while
|
|
return m_eof?-1:(NSV_NONSYNC_HEADERLEN_BITS- (int)(bs.avail())/8);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* NSV file header
|
|
4: NSV_HDR_DWORD
|
|
4: length of header in bytes
|
|
-- may not be 0 or 0xFFFFFFFF. :)
|
|
4: length of file, in bytes (including header - if this is 0 we are invalid)
|
|
-- can be 0xFFFFFFFF which means unknown length
|
|
4: length of file, in milliseconds (max file length, 24 days or so)
|
|
-- can be 0xFFFFFFFF which means unknown length
|
|
4: metadata length
|
|
4: number of TOC entries allocated
|
|
4: number of TOC entries used
|
|
mdlen: metadata
|
|
TOC_alloc*4:offset in file at time t.
|
|
*/
|
|
|
|
void nsv_writeheader(nsv_OutBS &bs, nsv_fileHeader *hdr, unsigned int padto)
|
|
{
|
|
if (hdr->toc_alloc < hdr->toc_size)
|
|
hdr->toc_alloc=hdr->toc_size;
|
|
|
|
if (hdr->toc_ex && hdr->toc_alloc <= hdr->toc_size*2)
|
|
hdr->toc_alloc=hdr->toc_size*2+1;
|
|
|
|
hdr->header_size = 4+4+4+4+4+hdr->metadata_len+4+4+4*hdr->toc_alloc;
|
|
|
|
bs.putbits(32,NSV_HDR_DWORD);
|
|
bs.putbits(32,hdr->header_size>padto?hdr->header_size:padto);
|
|
if (hdr->file_lenbytes == 0xFFFFFFFF) bs.putbits(32,hdr->file_lenbytes);
|
|
else bs.putbits(32,hdr->file_lenbytes+(hdr->header_size>padto?hdr->header_size:padto));
|
|
bs.putbits(32,hdr->file_lenms);
|
|
bs.putbits(32,hdr->metadata_len);
|
|
bs.putbits(32,hdr->toc_alloc);
|
|
bs.putbits(32,hdr->toc_size);
|
|
bs.putdata(hdr->metadata_len*8,hdr->metadata);
|
|
|
|
unsigned int numtoc=hdr->toc_alloc;
|
|
unsigned int numtocused=hdr->toc_size;
|
|
unsigned int *toc=hdr->toc;
|
|
unsigned int *toc_ex=hdr->toc_ex;
|
|
unsigned int numtocused2=(toc_ex && hdr->toc_alloc > hdr->toc_size*2) ? (hdr->toc_size + 1): 0;
|
|
|
|
while (numtoc--)
|
|
{
|
|
if (!numtocused)
|
|
{
|
|
if (numtocused2)
|
|
{
|
|
if (--numtocused2 == hdr->toc_size) // signal extended TOC :)
|
|
bs.putbits(32,NSV_MAKETYPE('T','O','C','2'));
|
|
else
|
|
bs.putbits(32,*toc_ex++);
|
|
}
|
|
else // extra (unused by this implementation but could be used someday so we fill it with 0xFF) space
|
|
bs.putbits(32,~0);
|
|
}
|
|
else if (toc)
|
|
{
|
|
bs.putbits(32,*toc++);
|
|
numtocused--;
|
|
}
|
|
else bs.putbits(32,0);
|
|
}
|
|
|
|
unsigned int x;
|
|
for (x = hdr->header_size; x < padto; x ++) bs.putbits(8,0);
|
|
}
|
|
|
|
|
|
int nsv_readheader(nsv_InBS &bs, nsv_fileHeader *hdr)
|
|
{
|
|
int s=0;
|
|
hdr->metadata=(void*)NULL;
|
|
hdr->toc=(unsigned int *)NULL;
|
|
hdr->toc_ex=(unsigned int *)NULL;
|
|
hdr->header_size=0;
|
|
hdr->file_lenbytes=~0;
|
|
hdr->file_lenms=~0;
|
|
hdr->toc_alloc=0;
|
|
hdr->toc_size=0;
|
|
hdr->metadata_len=0;
|
|
|
|
if (bs.avail()<64) {
|
|
return 8- (int)(bs.avail()/8);
|
|
}
|
|
s+=32;
|
|
if (bs.getbits(32) != NSV_HDR_DWORD)
|
|
{
|
|
bs.seek(-s);
|
|
return -1;
|
|
}else{
|
|
glCounterNSVf++;
|
|
}
|
|
s+=32;
|
|
unsigned int headersize=bs.getbits(32);
|
|
|
|
if (headersize >= 0x20000000)
|
|
{
|
|
bs.seek(-s);
|
|
return -1;
|
|
}
|
|
if ((unsigned int)bs.avail() < (headersize-4)*8)
|
|
{
|
|
int l=headersize-4- (int)(bs.avail()/8);
|
|
bs.seek(-s);
|
|
return l;
|
|
}
|
|
|
|
s+=32;
|
|
unsigned int lenbytes=bs.getbits(32);
|
|
s+=32;
|
|
unsigned int lenms=bs.getbits(32);
|
|
s+=32;
|
|
unsigned int metadatalen=bs.getbits(32);
|
|
s+=32;
|
|
unsigned int tocalloc=bs.getbits(32);
|
|
s+=32;
|
|
unsigned int tocsize=bs.getbits(32);
|
|
|
|
if (tocalloc < tocsize || lenbytes < headersize || tocalloc + metadatalen + s/8 > headersize)
|
|
{
|
|
bs.seek(-s);
|
|
return -1;
|
|
}
|
|
|
|
void *metadata=NULL;
|
|
|
|
if (metadatalen)
|
|
{
|
|
if (metadatalen > (SIZE_MAX/8))
|
|
{
|
|
bs.seek(-s);
|
|
return -1;
|
|
}
|
|
metadata=malloc(metadatalen+1);
|
|
if (!metadata)
|
|
{
|
|
bs.seek(-s);
|
|
return -1;
|
|
}
|
|
s+=metadatalen*8;
|
|
bs.getdata(metadatalen*8,metadata);
|
|
((char*)metadata)[metadatalen]=0;
|
|
}
|
|
|
|
unsigned int *toc=NULL;
|
|
unsigned int *toc_ex=NULL;
|
|
|
|
if (tocalloc && tocsize < (SIZE_MAX/8))
|
|
{
|
|
toc=(unsigned int *)malloc(tocsize * 4 * 2);
|
|
if (!toc)
|
|
{
|
|
free(metadata);
|
|
bs.seek(-s);
|
|
return -1;
|
|
}
|
|
unsigned int x;
|
|
int bitsread=0;
|
|
for (x = 0; x < tocsize; x ++) { toc[x] = bs.getbits(32); bitsread += 32; }
|
|
|
|
if (tocalloc > tocsize*2)
|
|
{
|
|
bitsread += 32;
|
|
if (bs.getbits(32) == NSV_MAKETYPE('T','O','C','2'))
|
|
{
|
|
toc_ex=toc + tocsize;
|
|
for (x = 0; x < tocsize; x ++) { toc_ex[x] = bs.getbits(32); bitsread += 32; }
|
|
}
|
|
}
|
|
bs.seek((tocalloc-tocsize)*32 - bitsread);
|
|
s+=tocalloc*32;
|
|
}
|
|
|
|
hdr->header_size=headersize;
|
|
if (lenbytes == 0xFFFFFFFF)
|
|
hdr->file_lenbytes=lenbytes;
|
|
else
|
|
hdr->file_lenbytes=lenbytes-headersize;
|
|
hdr->file_lenms=lenms;
|
|
hdr->metadata=metadata;
|
|
hdr->metadata_len=metadatalen;
|
|
hdr->toc=toc;
|
|
hdr->toc_ex=toc_ex;
|
|
hdr->toc_alloc=tocalloc;
|
|
hdr->toc_size=tocsize;
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *nsv_getmetadata(void *metadata, char *name)
|
|
{
|
|
if (!metadata) return NULL;
|
|
char *p=(char*)metadata;
|
|
size_t ln=strlen(name);
|
|
for (;;)
|
|
{
|
|
while (p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
|
|
if (p || !*p) break;
|
|
if (!_strnicmp(p,name,ln) && p[ln]=='=' && p[ln+1] && p[ln+2])
|
|
{
|
|
int cnt=0;
|
|
char *np=p+ln+1;
|
|
char c=*np++;
|
|
while (np[cnt] && np[cnt] != c) cnt++;
|
|
|
|
char *s=(char*)malloc(cnt+1);
|
|
if (!s) return NULL;
|
|
memcpy(s,np,cnt);
|
|
s[cnt]=0;
|
|
return s;
|
|
}
|
|
|
|
// advance to next item
|
|
while (p && *p && *p != '=') p++;
|
|
if (!*p++) break;
|
|
if (!*p) break;
|
|
char c=*p++;
|
|
while (p && *p && *p != c) p++;
|
|
if (*p) p++;
|
|
}
|
|
return NULL;
|
|
}
|