]> de.git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
.dpv video playback is now implemented. Slow though, because it's resizing textures...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 25 Jul 2002 09:14:48 +0000 (09:14 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 25 Jul 2002 09:14:48 +0000 (09:14 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@2083 d7cf8633-e32d-0410-b094-e92efae38249

20 files changed:
cl_main.c
cl_screen.c
cl_screen.h
cl_video.c [new file with mode: 0644]
cl_video.h [new file with mode: 0644]
darkplaces.dsp
dpvsimpledecode.c [new file with mode: 0644]
dpvsimpledecode.h [new file with mode: 0644]
draw.h
gl_draw.c
gl_textures.c
host.c
makefile
makefile.mingw
menu.c
r_textures.h
snd_dma.c
sound.h
wavefile.c [new file with mode: 0644]
wavefile.h [new file with mode: 0644]

index 4f83e460c8562db4ff1b648539449ddcbabf4cde..f64a4e057c5e9583930f30902234539c8e01d6bd 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 #include "cl_collision.h"
+#include "cl_video.h"
 
 // we need to declare some mouse variables here, because the menu system
 // references them even when on a unix system.
@@ -975,5 +976,7 @@ void CL_Init (void)
        CL_Particles_Init();
        CL_Screen_Init();
        CL_CGVM_Init();
+
+       CL_Video_Init();
 }
 
index 36477292f811612384a0457d7289f1311efc9d9e..9feb591a2f27d5b7452513721ca1e5d58b83d6de 100644 (file)
@@ -1,5 +1,6 @@
 
 #include "quakedef.h"
+#include "cl_video.h"
 
 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
 cvar_t scr_fov = {CVAR_SAVE, "fov","90"};      // 10 - 170
@@ -565,34 +566,41 @@ void DrawQ_Fill (float x, float y, float w, float h, float red, float green, flo
        r_refdef.drawqueuesize += dq->size;
 }
 
-//only used for the player color selection menu
-void DrawQ_PicTranslate (int x, int y, char *picname, qbyte *translation)
+void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
 {
-       int i, c;
-       unsigned int trans[4096];
-       cachepic_t *pic;
-
-       pic = Draw_CachePic(picname);
-       if (pic == NULL)
-               return;
-
-       c = pic->width * pic->height;
-       if (c > 4096)
-       {
-               Con_Printf("DrawQ_PicTranslate: image larger than 4k buffer\n");
+       int size;
+       void *p;
+       drawqueue_t *dq;
+       drawqueuemesh_t *m;
+       size = sizeof(*dq);
+       size += sizeof(drawqueuemesh_t);
+       size += sizeof(int) * mesh->numindices;
+       size += sizeof(float[3]) * mesh->numvertices;
+       size += sizeof(float[2]) * mesh->numvertices;
+       size += sizeof(float[4]) * mesh->numvertices;
+       if (r_refdef.drawqueuesize + size > MAX_DRAWQUEUE)
                return;
-       }
-
-       for (i = 0;i < c;i++)
-               trans[i] = d_8to24table[translation[menuplyr_pixels[i]]];
-
-       // FIXME: this is renderer stuff?
-       R_UpdateTexture (pic->tex, (qbyte *)trans);
-
-       DrawQ_Pic(x, y, picname, 0, 0, 1, 1, 1, 1, 0);
+       dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
+       dq->size = size;
+       dq->command = DRAWQUEUE_MESH;
+       dq->flags = flags;
+       dq->color = 0;
+       dq->x = 0;
+       dq->y = 0;
+       dq->scalex = 0;
+       dq->scaley = 0;
+       p = (void *)(dq + 1);
+       m = p;p += sizeof(drawqueuemesh_t);
+       m->numindices = mesh->numindices;
+       m->numvertices = mesh->numvertices;
+       m->texture = mesh->texture;
+       m->indices   = p;memcpy(m->indices  , mesh->indices  , m->numindices  * sizeof(int     ));p += m->numindices  * sizeof(int     );
+       m->vertices  = p;memcpy(m->vertices , mesh->vertices , m->numvertices * sizeof(float[3]));p += m->numvertices * sizeof(float[3]);
+       m->texcoords = p;memcpy(m->texcoords, mesh->texcoords, m->numvertices * sizeof(float[2]));p += m->numvertices * sizeof(float[2]);
+       m->colors    = p;memcpy(m->colors   , mesh->colors   , m->numvertices * sizeof(float[4]));p += m->numvertices * sizeof(float[4]);
+       r_refdef.drawqueuesize += dq->size;
 }
 
-
 /*
 ====================
 CalcFov
@@ -960,6 +968,8 @@ void CL_UpdateScreen(void)
                SCR_DrawLoading();
        }
 
+       CL_DrawVideo();
+
        R_TimeReport("2d");
 
        // add r_speeds text to queue
index 42575f0341028cc75850d516524ff7b78aec73cd..57552be4dd1f0210cd5512f3a76d637981a5b3a6 100644 (file)
@@ -7,6 +7,7 @@
 
 #define DRAWQUEUE_PIC 0
 #define DRAWQUEUE_STRING 1
+#define DRAWQUEUE_MESH 2
 
 typedef struct drawqueue_s
 {
@@ -17,14 +18,31 @@ typedef struct drawqueue_s
 }
 drawqueue_t;
 
+// a triangle mesh... embedded in the drawqueue
+typedef struct drawqueuemesh_s
+{
+       rtexture_t *texture;
+       int numindices;
+       int numvertices;
+       int *indices;
+       float *vertices;
+       float *texcoords;
+       qbyte *colors;
+}
+drawqueuemesh_t;
+
 #define DRAWFLAG_ADDITIVE 1
 
+// clear the draw queue
 void DrawQ_Clear(void);
+// draw an image
 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags);
+// draw a text string
 void DrawQ_String(float x, float y, char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags);
+// draw a filled rectangle
 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags);
-// only used for player config menu
-void DrawQ_PicTranslate (int x, int y, char *picname, qbyte *translation);
+// draw a triangle mesh
+void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags);
 
 void SHOWLMP_decodehide(void);
 void SHOWLMP_decodeshow(void);
diff --git a/cl_video.c b/cl_video.c
new file mode 100644 (file)
index 0000000..fd4c707
--- /dev/null
@@ -0,0 +1,243 @@
+
+#include "quakedef.h"
+#include "cl_video.h"
+#include "dpvsimpledecode.h"
+
+mempool_t *clvideomempool;
+
+int cl_videoplaying = false;
+void *cl_videostream;
+
+double cl_videostarttime;
+int cl_videoframenum;
+double cl_videoframerate;
+
+int cl_videoimagewidth;
+int cl_videoimageheight;
+int cl_videoimagedata_rmask;
+int cl_videoimagedata_gmask;
+int cl_videoimagedata_bmask;
+int cl_videoimagedata_bytesperpixel;
+void *cl_videoimagedata;
+
+int cl_videosoundrate;
+int cl_videosoundlength;
+short *cl_videosounddata;
+int cl_videosoundresamplelength;
+short *cl_videosoundresampledata;
+
+rtexture_t *cl_videotexture;
+rtexturepool_t *cl_videotexturepool;
+
+void CL_VideoFrame(void)
+{
+       int frames, framenum, samples, s;
+       if (!cl_videoplaying)
+               return;
+       framenum = (realtime - cl_videostarttime) * cl_videoframerate;
+       //Con_Printf("frame %i\n", framenum);
+       if (framenum < 0)
+               framenum = 0;
+       frames = 0;
+       while (cl_videoframenum < framenum)
+       {
+               frames++;
+               cl_videoframenum++;
+               if (dpvsimpledecode_video(cl_videostream, cl_videoimagedata, cl_videoimagedata_rmask, cl_videoimagedata_gmask, cl_videoimagedata_bmask, cl_videoimagedata_bytesperpixel, cl_videoimagewidth * cl_videoimagedata_bytesperpixel))
+               {
+                       CL_VideoStop();
+                       return;
+               }
+       }
+       if (frames)
+       {
+               R_UpdateTexture(cl_videotexture, cl_videoimagedata);
+               //Draw_NewPic("engine_videoframe", cl_videoimagewidth, cl_videoimageheight, false, cl_videoimagedata);
+       }
+       if (cl_videosoundrate && (samples = S_RawSamples_QueueWantsMore()))
+       {
+               Con_Printf("%i = S_RawSamples_QueueWantsMore()\n", samples);
+
+               // calculate how much source data we need to fill the output...
+               s = samples * cl_videosoundrate / shm->speed;
+
+               // reallocate processing buffer if needed
+               if (cl_videosoundresamplelength < samples)
+               {
+                       cl_videosoundresamplelength = samples + 100;
+                       if (cl_videosoundresampledata)
+                               Mem_Free(cl_videosoundresampledata);
+                       cl_videosoundresampledata = Mem_Alloc(clvideomempool, cl_videosoundresamplelength * sizeof(short[2]));
+               }
+
+               // reallocate loading buffer if needed
+               if (cl_videosoundlength < s)
+               {
+                       cl_videosoundlength = s + 100;
+                       if (cl_videosounddata)
+                               Mem_Free(cl_videosounddata);
+                       cl_videosounddata = Mem_Alloc(clvideomempool, cl_videosoundlength * sizeof(short[2]));
+               }
+
+               dpvsimpledecode_audio(cl_videostream, cl_videosounddata, s);
+               S_ResampleBuffer16Stereo(cl_videosounddata, s, cl_videosoundresampledata, samples);
+               S_RawSamples_Enqueue(cl_videosoundresampledata, samples);
+       }
+}
+
+void CL_DrawVideo(void)
+{
+       if (cl_videoplaying)
+       {
+               drawqueuemesh_t mesh;
+               int indices[6];
+               float vertices[12];
+               float texcoords[8];
+               qbyte colorsb[16];
+               float s1, t1, s2, t2, x1, y1, x2, y2;
+               indices[0] = 0;
+               indices[1] = 1;
+               indices[2] = 2;
+               indices[3] = 0;
+               indices[4] = 2;
+               indices[5] = 3;
+               x1 = 0;
+               y1 = 0;
+               x2 = vid.conwidth;
+               y2 = vid.conheight;
+               vertices[0] = y1;
+               vertices[1] = y1;
+               vertices[2] = 0;
+               vertices[3] = x2;
+               vertices[4] = y1;
+               vertices[5] = 0;
+               vertices[6] = x2;
+               vertices[7] = y2;
+               vertices[8] = 0;
+               vertices[9] = x1;
+               vertices[10] = y2;
+               vertices[11] = 0;
+               R_FragmentLocation(cl_videotexture, NULL, NULL, &s1, &t1, &s2, &t2);
+               texcoords[0] = s1;
+               texcoords[1] = t1;
+               texcoords[2] = s2;
+               texcoords[3] = t1;
+               texcoords[4] = s2;
+               texcoords[5] = t2;
+               texcoords[6] = s1;
+               texcoords[7] = t2;
+               colorsb[0] = 255 >> v_overbrightbits.integer;
+               colorsb[1] = 255 >> v_overbrightbits.integer;
+               colorsb[2] = 255 >> v_overbrightbits.integer;
+               colorsb[3] = 255;
+               colorsb[4] = 255 >> v_overbrightbits.integer;
+               colorsb[5] = 255 >> v_overbrightbits.integer;
+               colorsb[6] = 255 >> v_overbrightbits.integer;
+               colorsb[7] = 255;
+               colorsb[8] = 255 >> v_overbrightbits.integer;
+               colorsb[9] = 255 >> v_overbrightbits.integer;
+               colorsb[10] = 255 >> v_overbrightbits.integer;
+               colorsb[11] = 255;
+               colorsb[12] = 255 >> v_overbrightbits.integer;
+               colorsb[13] = 255 >> v_overbrightbits.integer;
+               colorsb[14] = 255 >> v_overbrightbits.integer;
+               colorsb[15] = 255;
+               mesh.texture = cl_videotexture;
+               mesh.numindices = 6;
+               mesh.numvertices = 4;
+               mesh.indices = indices;
+               mesh.vertices = vertices;
+               mesh.texcoords = texcoords;
+               mesh.colors = colorsb;
+               DrawQ_Mesh(&mesh, 0);
+               //DrawQ_Pic(0, 0, "engine_videoframe", vid.conwidth, vid.conheight, 1, 1, 1, 1, 0);
+       }
+}
+
+void CL_VideoStart(char *filename)
+{
+       char *errorstring;
+       cl_videostream = dpvsimpledecode_open(filename, &errorstring);
+       if (!cl_videostream)
+       {
+               Con_Printf("unable to open \"%s\", error: %s\n", filename, errorstring);
+               return;
+       }
+
+       cl_videoplaying = true;
+       cl_videostarttime = realtime;
+       cl_videoframenum = -1;
+       cl_videoframerate = dpvsimpledecode_getframerate(cl_videostream);
+       cl_videoimagewidth = dpvsimpledecode_getwidth(cl_videostream);
+       cl_videoimageheight = dpvsimpledecode_getheight(cl_videostream);
+
+       // RGBA format
+       cl_videoimagedata_bytesperpixel = 4;
+       cl_videoimagedata_rmask = BigLong(0xFF000000);
+       cl_videoimagedata_gmask = BigLong(0x00FF0000);
+       cl_videoimagedata_bmask = BigLong(0x0000FF00);
+       cl_videoimagedata = Mem_Alloc(clvideomempool, cl_videoimagewidth * cl_videoimageheight * cl_videoimagedata_bytesperpixel);
+       //memset(cl_videoimagedata, 97, cl_videoimagewidth * cl_videoimageheight * cl_videoimagedata_bytesperpixel);
+
+       cl_videosoundrate = dpvsimpledecode_getsoundrate(cl_videostream);
+       cl_videosoundlength = 0;
+       cl_videosounddata = NULL;
+       cl_videosoundresamplelength = 0;
+       cl_videosoundresampledata = NULL;
+
+       cl_videotexturepool = R_AllocTexturePool();
+       cl_videotexture = R_LoadTexture(cl_videotexturepool, "videotexture", cl_videoimagewidth, cl_videoimageheight, NULL, TEXTYPE_RGBA, 0);
+}
+
+void CL_VideoStop(void)
+{
+       cl_videoplaying = false;
+
+       if (cl_videostream)
+               dpvsimpledecode_close(cl_videostream);
+       cl_videostream = NULL;
+
+       if (cl_videoimagedata)
+               Mem_Free(cl_videoimagedata);
+       cl_videoimagedata = NULL;
+
+       if (cl_videosounddata)
+               Mem_Free(cl_videosounddata);
+       cl_videosounddata = NULL;
+
+       if (cl_videosoundresampledata)
+               Mem_Free(cl_videosoundresampledata);
+       cl_videosoundresampledata = NULL;
+
+       cl_videotexture = NULL;
+       R_FreeTexturePool(&cl_videotexturepool);
+
+       Draw_FreePic("engine_videoframe");
+}
+
+static void CL_PlayVideo_f(void)
+{
+       char name[1024];
+
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf ("usage: playvideo <videoname>\nplays video named video/<videoname>.dpv\n");
+               return;
+       }
+
+       sprintf(name, "%s/video/%s.dpv", com_gamedir, Cmd_Argv(1));
+       CL_VideoStart(name);
+}
+
+static void CL_StopVideo_f(void)
+{
+       CL_VideoStop();
+}
+
+void CL_Video_Init(void)
+{
+       Cmd_AddCommand("playvideo", CL_PlayVideo_f);
+       Cmd_AddCommand("stopvideo", CL_StopVideo_f);
+
+       clvideomempool = Mem_AllocPool("CL_Video");
+}
diff --git a/cl_video.h b/cl_video.h
new file mode 100644 (file)
index 0000000..58738af
--- /dev/null
@@ -0,0 +1,12 @@
+
+#ifndef CL_VIDEO_H
+#define CL_VIDEO_H
+
+extern int cl_videoplaying;
+void CL_VideoFrame(void);
+void CL_DrawVideo(void);
+void CL_VideoStart(char *filename);
+void CL_VideoStop(void);
+void CL_Video_Init(void);
+
+#endif
index 5f373267150a92b1755fe2a1924fc21686bb288f..309fe914f9b7f938f5cb69a2669a72627340f7cb 100644 (file)
@@ -7,14 +7,14 @@
 CFG=darkplaces - Win32 Debug\r
 !MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
 !MESSAGE use the Export Makefile command and run\r
-!MESSAGE \r
+!MESSAGE\r
 !MESSAGE NMAKE /f "darkplaces.mak".\r
-!MESSAGE \r
+!MESSAGE\r
 !MESSAGE You can specify a configuration when running NMAKE\r
 !MESSAGE by defining the macro CFG on the command line. For example:\r
-!MESSAGE \r
+!MESSAGE\r
 !MESSAGE NMAKE /f "darkplaces.mak" CFG="darkplaces - Win32 Debug"\r
-!MESSAGE \r
+!MESSAGE\r
 !MESSAGE Possible choices for configuration are:\r
 !MESSAGE \r
 !MESSAGE "darkplaces - Win32 Release" (based on "Win32 (x86) Application")\r
@@ -148,6 +148,10 @@ SOURCE=.\cl_tent.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\cl_video.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\cmd.c\r
 # End Source File\r
 # Begin Source File\r
@@ -176,6 +180,10 @@ SOURCE=.\cvar.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\dpvsimpledecode.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\filematch.c\r
 # End Source File\r
 # Begin Source File\r
@@ -412,6 +420,10 @@ SOURCE=.\wad.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\wavefile.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\world.c\r
 # End Source File\r
 # Begin Source File\r
@@ -456,6 +468,10 @@ SOURCE=.\cl_screen.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\cl_video.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\client.h\r
 # End Source File\r
 # Begin Source File\r
@@ -488,6 +504,10 @@ SOURCE=.\cvar.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\dpvsimpledecode.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\draw.h\r
 # End Source File\r
 # Begin Source File\r
@@ -676,6 +696,10 @@ SOURCE=.\wad.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\wavefile.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\winquake.h\r
 # End Source File\r
 # Begin Source File\r
diff --git a/dpvsimpledecode.c b/dpvsimpledecode.c
new file mode 100644 (file)
index 0000000..bdfcf31
--- /dev/null
@@ -0,0 +1,708 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "dpvsimpledecode.h"
+#include "wavefile.h"
+
+#define EMBEDDEDHZREAD 1
+
+#ifndef EMBEDDEDHZREAD
+#include "hz_read.h"
+#include "hz_read.c"
+#else
+#define HZREADERROR_OK 0
+#define HZREADERROR_EOF 1
+#define HZREADERROR_MALLOCFAILED 2
+
+#define HZREADBLOCKSIZE 16000
+
+typedef struct
+{
+       FILE *file;
+       int endoffile;
+}
+hz_bitstream_read_t;
+
+typedef struct hz_bitstream_readblock_s
+{
+       struct hz_bitstream_readblock_s *next;
+       unsigned int size;
+       unsigned char data[HZREADBLOCKSIZE];
+}
+hz_bitstream_readblock_t;
+
+typedef struct
+{
+       hz_bitstream_readblock_t *blocks;
+       hz_bitstream_readblock_t *current;
+       unsigned int position;
+       unsigned int store;
+       int count;
+}
+hz_bitstream_readblocks_t;
+
+hz_bitstream_read_t *hz_bitstream_read_open(char *filename)
+{
+       FILE *file;
+       hz_bitstream_read_t *stream;
+       if ((file = fopen(filename, "rb")))
+       {
+               stream = malloc(sizeof(hz_bitstream_read_t));
+               memset(stream, 0, sizeof(*stream));
+               stream->file = file;
+               return stream;
+       }
+       else
+               return NULL;
+}
+
+void hz_bitstream_read_close(hz_bitstream_read_t *stream)
+{
+       if (stream)
+       {
+               fclose(stream->file);
+               free(stream);
+       }
+}
+
+unsigned int hz_bitstream_read_currentbyte(hz_bitstream_read_t *stream)
+{
+       return ftell(stream->file);
+}
+
+int hz_bitstream_read_seek(hz_bitstream_read_t *stream, unsigned int position)
+{
+       stream->endoffile = 0;
+       return fseek(stream->file, position, SEEK_SET) != 0;
+}
+
+hz_bitstream_readblocks_t *hz_bitstream_read_blocks_new(void)
+{
+       hz_bitstream_readblocks_t *blocks;
+       blocks = malloc(sizeof(hz_bitstream_readblocks_t));
+       if (blocks == NULL)
+               return NULL;
+       memset(blocks, 0, sizeof(hz_bitstream_readblocks_t));
+       return blocks;
+}
+
+void hz_bitstream_read_blocks_free(hz_bitstream_readblocks_t *blocks)
+{
+       hz_bitstream_readblock_t *b, *n;
+       if (blocks == NULL)
+               return;
+       for (b = blocks->blocks;b;b = n)
+       {
+               n = b->next;
+               free(b);
+       }
+       free(blocks);
+}
+
+void hz_bitstream_read_flushbits(hz_bitstream_readblocks_t *blocks)
+{
+       blocks->store = 0;
+       blocks->count = 0;
+}
+
+int hz_bitstream_read_blocks_read(hz_bitstream_readblocks_t *blocks, hz_bitstream_read_t *stream, unsigned int size)
+{
+       int s;
+       hz_bitstream_readblock_t *b, *p;
+       s = size;
+       p = NULL;
+       b = blocks->blocks;
+       while (s > 0)
+       {
+               if (b == NULL)
+               {
+                       b = malloc(sizeof(hz_bitstream_readblock_t));
+                       if (b == NULL)
+                               return HZREADERROR_MALLOCFAILED;
+                       b->next = NULL;
+                       b->size = 0;
+                       if (p != NULL)
+                               p->next = b;
+                       else
+                               blocks->blocks = b;
+               }
+               if (s > HZREADBLOCKSIZE)
+                       b->size = HZREADBLOCKSIZE;
+               else
+                       b->size = s;
+               s -= b->size;
+               if (fread(b->data, 1, b->size, stream->file) != b->size)
+               {
+                       stream->endoffile = 1;
+                       break;
+               }
+               p = b;
+               b = b->next;
+       }
+       while (b)
+       {
+               b->size = 0;
+               b = b->next;
+       }
+       blocks->current = blocks->blocks;
+       blocks->position = 0;
+       hz_bitstream_read_flushbits(blocks);
+       if (stream->endoffile)
+               return HZREADERROR_EOF;
+       return HZREADERROR_OK;
+}
+
+unsigned int hz_bitstream_read_blocks_getbyte(hz_bitstream_readblocks_t *blocks)
+{
+       while (blocks->current != NULL && blocks->position >= blocks->current->size)
+       {
+               blocks->position = 0;
+               blocks->current = blocks->current->next;
+       }
+       if (blocks->current == NULL)
+               return 0;
+       return blocks->current->data[blocks->position++];
+}
+
+int hz_bitstream_read_bit(hz_bitstream_readblocks_t *blocks)
+{
+       if (!blocks->count)
+       {
+               blocks->count += 8;
+               blocks->store <<= 8;
+               blocks->store |= hz_bitstream_read_blocks_getbyte(blocks) & 0xFF;
+       }
+       blocks->count--;
+       return (blocks->store >> blocks->count) & 1;
+}
+
+unsigned int hz_bitstream_read_bits(hz_bitstream_readblocks_t *blocks, unsigned int size)
+{
+       unsigned int num = 0;
+       // we can only handle about 24 bits at a time safely
+       // (there might be up to 7 bits more than we need in the bit store)
+       if (size > 24)
+       {
+               size -= 8;
+               num |= hz_bitstream_read_bits(blocks, 8) << size;
+       }
+       while (blocks->count < size)
+       {
+               blocks->count += 8;
+               blocks->store <<= 8;
+               blocks->store |= hz_bitstream_read_blocks_getbyte(blocks) & 0xFF;
+       }
+       blocks->count -= size;
+       num |= (blocks->store >> blocks->count) & ((1 << size) - 1);
+       return num;
+}
+
+unsigned int hz_bitstream_read_byte(hz_bitstream_readblocks_t *blocks)
+{
+       return hz_bitstream_read_blocks_getbyte(blocks);
+}
+
+unsigned int hz_bitstream_read_short(hz_bitstream_readblocks_t *blocks)
+{
+       return (hz_bitstream_read_byte(blocks) << 8)
+            | (hz_bitstream_read_byte(blocks));
+}
+
+unsigned int hz_bitstream_read_int(hz_bitstream_readblocks_t *blocks)
+{
+       return (hz_bitstream_read_byte(blocks) << 24)
+            | (hz_bitstream_read_byte(blocks) << 16)
+            | (hz_bitstream_read_byte(blocks) << 8)
+            | (hz_bitstream_read_byte(blocks));
+}
+
+void hz_bitstream_read_bytes(hz_bitstream_readblocks_t *blocks, void *outdata, unsigned int size)
+{
+       unsigned char *out;
+       out = outdata;
+       while (size--)
+               *out++ = hz_bitstream_read_byte(blocks);
+}
+#endif
+
+#define BLOCKSIZE 8
+
+typedef struct dpvsimpledecodestream_s
+{
+       hz_bitstream_read_t *bitstream;
+       hz_bitstream_readblocks_t *framedatablocks;
+
+       int error;
+
+       double info_framerate;
+       unsigned int info_frames;
+
+       unsigned int info_imagewidth;
+       unsigned int info_imageheight;
+       unsigned int info_imagebpp;
+       unsigned int info_imageRloss;
+       unsigned int info_imageRmask;
+       unsigned int info_imageRshift;
+       unsigned int info_imageGloss;
+       unsigned int info_imageGmask;
+       unsigned int info_imageGshift;
+       unsigned int info_imageBloss;
+       unsigned int info_imageBmask;
+       unsigned int info_imageBshift;
+       unsigned int info_imagesize;
+
+       // current video frame (needed because of delta compression)
+       int videoframenum;
+       // current video frame data (needed because of delta compression)
+       unsigned int *videopixels;
+
+       // wav file the sound is being read from
+       wavefile_t *wavefile;
+}
+dpvsimpledecodestream_t;
+
+static int dpvsimpledecode_setpixelformat(dpvsimpledecodestream_t *s, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel)
+{
+       int Rshift, Rbits, Gshift, Gbits, Bshift, Bbits;
+       if (!Rmask)
+       {
+               s->error = DPVSIMPLEDECODEERROR_INVALIDRMASK;
+               return s->error;
+       }
+       if (!Gmask)
+       {
+               s->error = DPVSIMPLEDECODEERROR_INVALIDGMASK;
+               return s->error;
+       }
+       if (!Bmask)
+       {
+               s->error = DPVSIMPLEDECODEERROR_INVALIDBMASK;
+               return s->error;
+       }
+       if (Rmask & Gmask || Rmask & Bmask || Gmask & Bmask)
+       {
+               s->error = DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP;
+               return s->error;
+       }
+       switch (bytesperpixel)
+       {
+       case 2:
+               if ((Rmask | Gmask | Bmask) > 65536)
+               {
+                       s->error = DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP;
+                       return s->error;
+               }
+               break;
+       case 4:
+               break;
+       default:
+               s->error = DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP;
+               return s->error;
+               break;
+       }
+       for (Rshift = 0;!(Rmask & 1);Rshift++, Rmask >>= 1);
+       for (Gshift = 0;!(Gmask & 1);Gshift++, Gmask >>= 1);
+       for (Bshift = 0;!(Bmask & 1);Bshift++, Bmask >>= 1);
+       if (((Rmask + 1) & Rmask) != 0)
+       {
+               s->error = DPVSIMPLEDECODEERROR_INVALIDRMASK;
+               return s->error;
+       }
+       if (((Gmask + 1) & Gmask) != 0)
+       {
+               s->error = DPVSIMPLEDECODEERROR_INVALIDGMASK;
+               return s->error;
+       }
+       if (((Bmask + 1) & Bmask) != 0)
+       {
+               s->error = DPVSIMPLEDECODEERROR_INVALIDBMASK;
+               return s->error;
+       }
+       for (Rbits = 0;Rmask & 1;Rbits++, Rmask >>= 1);
+       for (Gbits = 0;Gmask & 1;Gbits++, Gmask >>= 1);
+       for (Bbits = 0;Bmask & 1;Bbits++, Bmask >>= 1);
+       if (Rbits > 8)
+       {
+               Rshift += (Rbits - 8);
+               Rbits = 8;
+       }
+       if (Gbits > 8)
+       {
+               Gshift += (Gbits - 8);
+               Gbits = 8;
+       }
+       if (Bbits > 8)
+       {
+               Bshift += (Bbits - 8);
+               Bbits = 8;
+       }
+       s->info_imagebpp = bytesperpixel;
+       s->info_imageRloss = 16 + (8 - Rbits);
+       s->info_imageGloss =  8 + (8 - Gbits);
+       s->info_imageBloss =  0 + (8 - Bbits);
+       s->info_imageRmask = (1 << Rbits) - 1;
+       s->info_imageGmask = (1 << Gbits) - 1;
+       s->info_imageBmask = (1 << Bbits) - 1;
+       s->info_imageRshift = Rshift;
+       s->info_imageGshift = Gshift;
+       s->info_imageBshift = Bshift;
+       s->info_imagesize = s->info_imagewidth * s->info_imageheight * s->info_imagebpp;
+       return s->error;
+}
+
+// opening and closing streams
+
+static void StripExtension(char *in, char *out)
+{
+       char *dot, *c;
+       dot = NULL;
+       for (c = in;*c;c++)
+       {
+               if (*c == ':' || *c == '\\' || *c == '/')
+                       dot = NULL;
+               if (*c == '.')
+                       dot = c;
+       }
+       if (dot == NULL)
+       {
+               // nothing to remove
+               strcpy(out, in);
+               return;
+       }
+       else
+       {
+               memcpy(out, in, dot - in);
+               out[dot - in] = 0;
+       }
+}
+
+// opens a stream
+void *dpvsimpledecode_open(char *filename, char **errorstring)
+{
+       dpvsimpledecodestream_t *s;
+       char t[8], *wavename;
+       if (errorstring != NULL)
+               *errorstring = NULL;
+       s = malloc(sizeof(dpvsimpledecodestream_t));
+       if (s != NULL)
+       {
+               s->bitstream = hz_bitstream_read_open(filename);
+               if (s->bitstream != NULL)
+               {
+                       // check file identification
+                       s->framedatablocks = hz_bitstream_read_blocks_new();
+                       if (s->framedatablocks != NULL)
+                       {
+                               hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 8);
+                               hz_bitstream_read_bytes(s->framedatablocks, t, 8);
+                               if (!memcmp(t, "DPVideo", 8))
+                               {
+                                       // check version number
+                                       hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 2);
+                                       if (hz_bitstream_read_short(s->framedatablocks) == 1)
+                                       {
+                                               hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 12);
+                                               s->info_imagewidth = hz_bitstream_read_short(s->framedatablocks);
+                                               s->info_imageheight = hz_bitstream_read_short(s->framedatablocks);
+                                               s->info_framerate = (double) hz_bitstream_read_int(s->framedatablocks) * (1.0 / 65536.0);
+
+                                               if (s->info_framerate > 0.0)
+                                               {
+                                                       s->videopixels = malloc(s->info_imagewidth * s->info_imageheight * sizeof(*s->videopixels));
+                                                       if (s->videopixels != NULL)
+                                                       {
+                                                               wavename = malloc(strlen(filename) + 10);
+                                                               if (wavename)
+                                                               {
+                                                                       StripExtension(filename, wavename);
+                                                                       strcat(wavename, ".wav");
+                                                                       s->wavefile = waveopen(wavename, NULL);
+                                                                       free(wavename);
+                                                               }
+                                                               // all is well...
+                                                               s->videoframenum = -10000;
+                                                               return s;
+                                                       }
+                                                       else if (errorstring != NULL)
+                                                               *errorstring = "unable to allocate video image buffer";
+                                               }
+                                               else if (errorstring != NULL)
+                                                       *errorstring = "error in video info chunk";
+                                       }
+                                       else if (errorstring != NULL)
+                                               *errorstring = "read error";
+                               }
+                               else if (errorstring != NULL)
+                                       *errorstring = "not a dpvideo file";
+                               hz_bitstream_read_blocks_free(s->framedatablocks);
+                       }
+                       else if (errorstring != NULL)
+                               *errorstring = "unable to allocate memory for reading buffer";
+                       hz_bitstream_read_close(s->bitstream);
+               }
+               else if (errorstring != NULL)
+                       *errorstring = "unable to open file";
+               free(s);
+       }
+       else if (errorstring != NULL)
+               *errorstring = "unable to allocate memory for stream info structure";
+       return NULL;
+}
+
+// closes a stream
+void dpvsimpledecode_close(void *stream)
+{
+       dpvsimpledecodestream_t *s = stream;
+       if (s == NULL)
+               return;
+       if (s->videopixels)
+               free(s->videopixels);
+       if (s->wavefile)
+               waveclose(s->wavefile);
+       if (s->framedatablocks)
+               hz_bitstream_read_blocks_free(s->framedatablocks);
+       if (s->bitstream)
+               hz_bitstream_read_close(s->bitstream);
+       free(s);
+}
+
+// utilitarian functions
+
+// returns the current error number for the stream, and resets the error
+// number to DPVSIMPLEDECODEERROR_NONE
+// if the supplied string pointer variable is not NULL, it will be set to the
+// error message
+int dpvsimpledecode_error(void *stream, char **errorstring)
+{
+       dpvsimpledecodestream_t *s = stream;
+       int e;
+       e = s->error;
+       s->error = 0;
+       if (errorstring)
+       {
+               switch (e)
+               {
+                       case DPVSIMPLEDECODEERROR_NONE:
+                               *errorstring = "no error";
+                               break;
+                       case DPVSIMPLEDECODEERROR_EOF:
+                               *errorstring = "end of file reached (this is not an error)";
+                               break;
+                       case DPVSIMPLEDECODEERROR_READERROR:
+                               *errorstring = "read error (corrupt or incomplete file)";
+                               break;
+                       case DPVSIMPLEDECODEERROR_SOUNDBUFFERTOOSMALL:
+                               *errorstring = "sound buffer is too small for decoding frame (please allocate it as large as dpvsimpledecode_getneededsoundbufferlength suggests)";
+                               break;
+                       case DPVSIMPLEDECODEERROR_INVALIDRMASK:
+                               *errorstring = "invalid red bits mask";
+                               break;
+                       case DPVSIMPLEDECODEERROR_INVALIDGMASK:
+                               *errorstring = "invalid green bits mask";
+                               break;
+                       case DPVSIMPLEDECODEERROR_INVALIDBMASK:
+                               *errorstring = "invalid blue bits mask";
+                               break;
+                       case DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP:
+                               *errorstring = "color bit masks overlap";
+                               break;
+                       case DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP:
+                               *errorstring = "color masks too big for specified bytes per pixel";
+                               break;
+                       case DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP:
+                               *errorstring = "unsupported bytes per pixel (must be 2 for 16bit, or 4 for 32bit)";
+                               break;
+                       default:
+                               *errorstring = "unknown error";
+                               break;
+               }
+       }
+       return e;
+}
+
+// returns the width of the image data
+unsigned int dpvsimpledecode_getwidth(void *stream)
+{
+       dpvsimpledecodestream_t *s = stream;
+       return s->info_imagewidth;
+}
+
+// returns the height of the image data
+unsigned int dpvsimpledecode_getheight(void *stream)
+{
+       dpvsimpledecodestream_t *s = stream;
+       return s->info_imageheight;
+}
+
+// returns the sound sample rate of the stream
+unsigned int dpvsimpledecode_getsoundrate(void *stream)
+{
+       dpvsimpledecodestream_t *s = stream;
+       if (s->wavefile)
+               return s->wavefile->info_rate;
+       else
+               return 0;
+}
+
+// returns the framerate of the stream
+double dpvsimpledecode_getframerate(void *stream)
+{
+       dpvsimpledecodestream_t *s = stream;
+       return s->info_framerate;
+}
+
+
+
+
+
+static int dpvsimpledecode_convertpixels(dpvsimpledecodestream_t *s, void *imagedata, int imagebytesperrow)
+{
+       unsigned int a, x, y, width, height;
+       unsigned int Rloss, Rmask, Rshift, Gloss, Gmask, Gshift, Bloss, Bmask, Bshift;
+       unsigned int *in;
+
+       width = s->info_imagewidth;
+       height = s->info_imageheight;
+
+       Rloss = s->info_imageRloss;
+       Rmask = s->info_imageRmask;
+       Rshift = s->info_imageRshift;
+       Gloss = s->info_imageGloss;
+       Gmask = s->info_imageGmask;
+       Gshift = s->info_imageGshift;
+       Bloss = s->info_imageBloss;
+       Bmask = s->info_imageBmask;
+       Bshift = s->info_imageBshift;
+
+       in = s->videopixels;
+       if (s->info_imagebpp == 4)
+       {
+               unsigned int *outrow;
+               for (y = 0;y < height;y++)
+               {
+                       outrow = (void *)((unsigned char *)imagedata + y * imagebytesperrow);
+                       for (x = 0;x < width;x++)
+                       {
+                               a = *in++;
+                               outrow[x] = (((a >> Rloss) & Rmask) << Rshift) | (((a >> Gloss) & Gmask) << Gshift) | (((a >> Bloss) & Bmask) << Bshift);
+                       }
+               }
+       }
+       else
+       {
+               unsigned short *outrow;
+               for (y = 0;y < height;y++)
+               {
+                       outrow = (void *)((unsigned char *)imagedata + y * imagebytesperrow);
+                       if (Rloss == 19 && Gloss == 10 && Bloss == 3 && Rshift == 11 && Gshift == 5 && Bshift == 0)
+                       {
+                               // optimized
+                               for (x = 0;x < width;x++)
+                               {
+                                       a = *in++;
+                                       outrow[x] = ((a >> 8) & 0xF800) | ((a >> 5) & 0x07E0) | ((a >> 3) & 0x001F);
+                               }
+                       }
+                       else
+                       {
+                               for (x = 0;x < width;x++)
+                               {
+                                       a = *in++;
+                                       outrow[x] = (((a >> Rloss) & Rmask) << Rshift) | (((a >> Gloss) & Gmask) << Gshift) | (((a >> Bloss) & Bmask) << Bshift);
+                               }
+                       }
+               }
+       }
+       return s->error;
+}
+
+static int dpvsimpledecode_decompressimage(dpvsimpledecodestream_t *s)
+{
+       int i, a, b, colors, g, x1, y1, bw, bh, width, height, palettebits;
+       unsigned int palette[256], *outrow, *out;
+       g = BLOCKSIZE;
+       width = s->info_imagewidth;
+       height = s->info_imageheight;
+       for (y1 = 0;y1 < height;y1 += g)
+       {
+               outrow = s->videopixels + y1 * width;
+               bh = g;
+               if (y1 + bh > height)
+                       bh = height - y1;
+               for (x1 = 0;x1 < width;x1 += g)
+               {
+                       out = outrow + x1;
+                       bw = g;
+                       if (x1 + bw > width)
+                               bw = width - x1;
+                       if (hz_bitstream_read_bit(s->framedatablocks))
+                       {
+                               // updated block
+                               palettebits = hz_bitstream_read_bits(s->framedatablocks, 3);
+                               colors = 1 << palettebits;
+                               for (i = 0;i < colors;i++)
+                                       palette[i] = hz_bitstream_read_bits(s->framedatablocks, 24);
+                               if (palettebits)
+                               {
+                                       for (b = 0;b < bh;b++, out += width)
+                                               for (a = 0;a < bw;a++)
+                                                       out[a] = palette[hz_bitstream_read_bits(s->framedatablocks, palettebits)];
+                               }
+                               else
+                               {
+                                       for (b = 0;b < bh;b++, out += width)
+                                               for (a = 0;a < bw;a++)
+                                                       out[a] = palette[0];
+                               }
+                       }
+               }
+       }
+       return s->error;
+}
+
+// decodes a video frame to the supplied output pixels
+int dpvsimpledecode_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)
+{
+       dpvsimpledecodestream_t *s = stream;
+       unsigned int framedatasize;
+       char t[4];
+       s->error = DPVSIMPLEDECODEERROR_NONE;
+       if (dpvsimpledecode_setpixelformat(s, Rmask, Gmask, Bmask, bytesperpixel))
+               return s->error;
+
+       hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 8);
+       hz_bitstream_read_bytes(s->framedatablocks, t, 4);
+       if (memcmp(t, "VID0", 4))
+       {
+               if (t[0] == 0)
+                       return (s->error = DPVSIMPLEDECODEERROR_EOF);
+               else
+                       return (s->error = DPVSIMPLEDECODEERROR_READERROR);
+       }
+       framedatasize = hz_bitstream_read_int(s->framedatablocks);
+       hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, framedatasize);
+       if (dpvsimpledecode_decompressimage(s))
+               return s->error;
+
+       dpvsimpledecode_convertpixels(s, imagedata, imagebytesperrow);
+       return s->error;
+}
+
+// (note: sound is 16bit stereo native-endian, left channel first)
+int dpvsimpledecode_audio(void *stream, short *soundbuffer, int requestedlength)
+{
+       int samples;
+       dpvsimpledecodestream_t *s = stream;
+       s->error = DPVSIMPLEDECODEERROR_NONE;
+       if (requestedlength)
+       {
+               samples = 0;
+               if (s->wavefile && requestedlength)
+                       samples = waveread16stereo(s->wavefile, soundbuffer, requestedlength);
+               if (samples < requestedlength)
+                       memset(soundbuffer + samples * 2, 0, (requestedlength - samples) * sizeof(short[2]));
+       }
+       return s->error;
+}
diff --git a/dpvsimpledecode.h b/dpvsimpledecode.h
new file mode 100644 (file)
index 0000000..36e61a4
--- /dev/null
@@ -0,0 +1,49 @@
+
+#ifndef DPVSIMPLEDECODE_H
+#define DPVSIMPLEDECODE_H
+
+#define DPVSIMPLEDECODEERROR_NONE 0
+#define DPVSIMPLEDECODEERROR_EOF 1
+#define DPVSIMPLEDECODEERROR_READERROR 2
+#define DPVSIMPLEDECODEERROR_SOUNDBUFFERTOOSMALL 3
+#define DPVSIMPLEDECODEERROR_INVALIDRMASK 4
+#define DPVSIMPLEDECODEERROR_INVALIDGMASK 5
+#define DPVSIMPLEDECODEERROR_INVALIDBMASK 6
+#define DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP 7
+#define DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP 8
+#define DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP 9
+
+// opening and closing streams
+
+// opens a stream
+void *dpvsimpledecode_open(char *filename, char **errorstring);
+// closes a stream
+void dpvsimpledecode_close(void *stream);
+
+// utilitarian functions
+
+// returns the current error number for the stream, and resets the error
+// number to DPVDECODEERROR_NONE
+// if the supplied string pointer variable is not NULL, it will be set to the
+// error message
+int dpvsimpledecode_error(void *stream, char **errorstring);
+
+// returns the width of the image data
+unsigned int dpvsimpledecode_getwidth(void *stream);
+
+// returns the height of the image data
+unsigned int dpvsimpledecode_getheight(void *stream);
+
+// returns the sound sample rate of the stream
+unsigned int dpvsimpledecode_getsoundrate(void *stream);
+
+// returns the framerate of the stream
+double dpvsimpledecode_getframerate(void *stream);
+
+// decodes a video frame to the supplied output pixels
+int dpvsimpledecode_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);
+// reads some sound
+// (note: sound is 16bit stereo native-endian, left channel first)
+int dpvsimpledecode_audio(void *stream, short *soundbuffer, int requestedlength);
+
+#endif
diff --git a/draw.h b/draw.h
index 085259cb14a3565f324872e9c6d581b8c320e18d..bbf5a21be98e1155d633a4e416e8a483f1f2e899 100644 (file)
--- a/draw.h
+++ b/draw.h
@@ -40,6 +40,10 @@ cachepic_t;
 
 void Draw_Init (void);
 cachepic_t *Draw_CachePic (char *path);
+// create or update a pic's image
+cachepic_t *Draw_NewPic(char *picname, int width, int height, int alpha, qbyte *pixels);
+// free the texture memory used by a pic
+void Draw_FreePic(char *picname);
 
 void R_DrawQueue(void);
 
index de361fb0aef825a47574b00427c356dd41a3156b..3e1e21878ef5f347a52dd01405658c5023c82db8 100644 (file)
--- a/gl_draw.c
+++ b/gl_draw.c
@@ -259,6 +259,67 @@ cachepic_t *Draw_CachePic (char *path)
        return pic;
 }
 
+cachepic_t *Draw_NewPic(char *picname, int width, int height, int alpha, qbyte *pixels)
+{
+       int crc, hashkey;
+       cachepic_t *pic;
+
+       crc = CRC_Block(picname, strlen(picname));
+       hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
+       for (pic = cachepichash[hashkey];pic;pic = pic->chain)
+               if (!strcmp (picname, pic->name))
+                       break;
+
+       if (pic)
+       {
+               if (pic->tex && R_TextureWidth(pic->tex) == width && R_TextureHeight(pic->tex) == height && (R_TextureHasAlpha(pic->tex) != 0) == (alpha != 0))
+               {
+                       R_UpdateTexture(pic->tex, pixels);
+                       return pic;
+               }
+       }
+       else
+       {
+               if (pic == NULL)
+               {
+                       if (numcachepics == MAX_CACHED_PICS)
+                               Sys_Error ("numcachepics == MAX_CACHED_PICS");
+                       pic = cachepics + (numcachepics++);
+                       strcpy (pic->name, picname);
+                       // link into list
+                       pic->chain = cachepichash[hashkey];
+                       cachepichash[hashkey] = pic;
+               }
+       }
+
+       pic->width = width;
+       pic->height = height;
+       if (pic->tex)
+               R_FreeTexture(pic->tex);
+       pic->tex = R_LoadTexture (drawtexturepool, picname, width, height, pixels, TEXTYPE_RGBA, alpha ? TEXF_ALPHA : 0);
+       return pic;
+}
+
+void Draw_FreePic(char *picname)
+{
+       int crc;
+       int hashkey;
+       cachepic_t *pic;
+       // this doesn't really free the pic, but does free it's texture
+       crc = CRC_Block(picname, strlen(picname));
+       hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
+       for (pic = cachepichash[hashkey];pic;pic = pic->chain)
+       {
+               if (!strcmp (picname, pic->name))
+               {
+                       R_FreeTexture(pic->tex);
+                       pic->width = 0;
+                       pic->height = 0;
+                       return;
+               }
+       }
+}
+
 /*
 ===============
 Draw_Init
@@ -296,6 +357,7 @@ void GL_Draw_Init (void)
        R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
 }
 
+extern cvar_t gl_mesh_drawmode;
 void R_DrawQueue(void)
 {
        int pos, num, chartexnum, overbright;
@@ -305,6 +367,7 @@ void R_DrawQueue(void)
        char *str, *currentpic;
        int batch, batchcount, additive;
        unsigned int color;
+       drawqueuemesh_t *mesh;
 
        if (!r_render.integer)
                return;
@@ -479,6 +542,78 @@ void R_DrawQueue(void)
                                x += w;
                        }
                        break;
+               case DRAWQUEUE_MESH:
+                       if (batch)
+                       {
+                               batch = false;
+                               qglEnd();
+                       }
+                       mesh = (void *)(dq + 1);
+                       qglBindTexture(GL_TEXTURE_2D, R_GetTexture(mesh->texture));
+                       if (gl_mesh_drawmode.integer > 0)
+                       {
+                               qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), mesh->vertices);CHECKGLERROR
+                               qglTexCoordPointer(2, GL_FLOAT, sizeof(float[2]), mesh->texcoords);CHECKGLERROR
+                               qglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(qbyte[4]), mesh->colors);CHECKGLERROR
+                               qglEnableClientState(GL_VERTEX_ARRAY);CHECKGLERROR
+                               qglEnableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
+                               qglEnableClientState(GL_COLOR_ARRAY);CHECKGLERROR
+                       }
+                       if (gl_mesh_drawmode.integer == 3 && qglDrawRangeElements == NULL)
+                               Cvar_SetValueQuick(&gl_mesh_drawmode, 2);
+
+                       if (gl_mesh_drawmode.integer == 3)
+                       {
+                               // GL 1.2 or GL 1.1 with extension
+                               qglDrawRangeElements(GL_TRIANGLES, 0, mesh->numvertices, mesh->numindices, GL_UNSIGNED_INT, mesh->indices);
+                               CHECKGLERROR
+                       }
+                       else if (gl_mesh_drawmode.integer == 2)
+                       {
+                               // GL 1.1
+                               qglDrawElements(GL_TRIANGLES, mesh->numindices, GL_UNSIGNED_INT, mesh->indices);
+                               CHECKGLERROR
+                       }
+                       else if (gl_mesh_drawmode.integer == 1)
+                       {
+                               int i;
+                               // GL 1.1
+                               // feed it manually using glArrayElement
+                               qglBegin(GL_TRIANGLES);
+                               for (i = 0;i < mesh->numindices;i++)
+                                       qglArrayElement(mesh->indices[i]);
+                               qglEnd();
+                               CHECKGLERROR
+                       }
+                       else
+                       {
+                               int i, in;
+                               // GL 1.1 but not using vertex arrays - 3dfx glquake minigl driver
+                               // feed it manually
+                               if (gl_mesh_drawmode.integer != 0)
+                                       Cvar_SetValueQuick(&gl_mesh_drawmode, 0);
+                               qglBegin(GL_TRIANGLES);
+                               for (i = 0;i < mesh->numindices;i++)
+                               {
+                                       in = mesh->indices[i];
+                                       qglColor4ub(mesh->colors[in * 4], mesh->colors[in * 4 + 1], mesh->colors[in * 4 + 2], mesh->colors[in * 4 + 3]);
+                                       qglTexCoord2f(mesh->texcoords[in * 2], mesh->texcoords[in * 2 + 1]);
+                                       qglVertex3f(mesh->vertices[in * 3], mesh->vertices[in * 3 + 1], mesh->vertices[in * 3 + 2]);
+                               }
+                               qglEnd();
+                               CHECKGLERROR
+                       }
+                       if (gl_mesh_drawmode.integer > 0)
+                       {
+                               qglDisableClientState(GL_VERTEX_ARRAY);CHECKGLERROR
+                               qglDisableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
+                               qglDisableClientState(GL_COLOR_ARRAY);CHECKGLERROR
+                       }
+                       // restore color, since it got trashed by using color array
+                       qglColor4ub((qbyte)(((color >> 24) & 0xFF) >> overbright), (qbyte)(((color >> 16) & 0xFF) >> overbright), (qbyte)(((color >> 8) & 0xFF) >> overbright), (qbyte)(color & 0xFF));
+                       CHECKGLERROR
+                       currentpic = "\0";
+                       break;
                }
        }
        if (batch)
index 43bf88e8b8e5f1522cfba9f97f191102f82662cc..a2b005fae9391cd4a6554def3a746732c9d25edd 100644 (file)
@@ -201,12 +201,13 @@ int R_GetTexture(rtexture_t *rt)
        return glt->image->texnum;
 }
 
-static void R_FreeTexture(gltexture_t *glt)
+void R_FreeTexture(rtexture_t *rt)
 {
-       gltexture_t **gltpointer;
+       gltexture_t *glt, **gltpointer;
        gltextureimage_t *image, **gltimagepointer;
        GLuint texnum;
 
+       glt = (gltexture_t *)rt;
        if (glt == NULL)
                Host_Error("R_FreeTexture: texture == NULL\n");
 
@@ -293,7 +294,7 @@ void R_FreeTexturePool(rtexturepool_t **rtexturepool)
        else
                Host_Error("R_FreeTexturePool: pool not linked\n");
        while (pool->gltchain)
-               R_FreeTexture(pool->gltchain);
+               R_FreeTexture((rtexture_t *)pool->gltchain);
        if (pool->imagechain)
                Sys_Error("R_FreeTexturePool: not all images freed\n");
        Mem_Free(pool);
@@ -924,7 +925,7 @@ rtexture_t *R_LoadTexture (rtexturepool_t *rtexturepool, char *identifier, int w
                        return (rtexture_t *)glt; // exact match, use existing
                }
                Con_Printf("R_LoadTexture: cache mismatch on %s, replacing old texture\n", identifier);
-               R_FreeTexture(glt);
+               R_FreeTexture((rtexture_t *)glt);
        }
 
        return (rtexture_t *)R_SetupTexture(pool, identifier, crc, width, height, flags | GLTEXF_UPLOAD, texinfo, data, NULL, NULL, 0);
@@ -958,7 +959,7 @@ rtexture_t *R_ProceduralTexture (rtexturepool_t *rtexturepool, char *identifier,
                        return (rtexture_t *)glt; // exact match, use existing
                }
                Con_Printf("R_LoadTexture: cache mismatch, replacing old texture\n");
-               R_FreeTexture(glt);
+               R_FreeTexture((rtexture_t *)glt);
        }
 
        return (rtexture_t *)R_SetupTexture(pool, identifier, 0, width, height, flags | GLTEXF_PROCEDURAL | GLTEXF_UPLOAD, texinfo, NULL, generate, proceduraldata, proceduraldatasize);
diff --git a/host.c b/host.c
index 33797065f9c94fad79433abebee88c29805b6be6..c6ffa4aa8a472eabc7e9c0a4686b6b02b366aafb 100644 (file)
--- a/host.c
+++ b/host.c
@@ -19,8 +19,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 // host.c -- coordinates spawning and killing of local servers
 
-#include "quakedef.h"
 #include <time.h>
+#include "quakedef.h"
+#include "cl_video.h"
 
 /*
 
@@ -716,6 +717,8 @@ void _Host_Frame (float time)
 
        ui_update();
 
+       CL_VideoFrame();
+
 // update video
        if (host_speeds.integer)
                time1 = Sys_DoubleTime ();
index 5538b3f03822a85f26f58cd5ccb2bc6120ad6344..8feb568d95aa6bd5de46129c1f32e1e754a26d6e 100644 (file)
--- a/makefile
+++ b/makefile
@@ -22,7 +22,7 @@ SOUNDLIB=
 #if you want no CD audio
 CD=cd_null.o
 
-OBJECTS= builddate.o $(CD) $(SND) chase.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_tent.o cmd.o common.o console.o crc.o cvar.o fractalnoise.o gl_draw.o r_sky.o gl_rmain.o gl_rsurf.o host.o host_cmd.o image.o keys.o mathlib.o menu.o model_alias.o model_brush.o model_shared.o model_sprite.o net_bsd.o net_udp.o net_dgrm.o net_loop.o net_main.o pr_cmds.o pr_edict.o pr_exec.o r_light.o r_explosion.o sbar.o sv_main.o sv_move.o sv_phys.o sv_user.o sv_light.o sys_linux.o transform.o view.o wad.o world.o zone.o vid_shared.o palette.o r_crosshairs.o gl_textures.o gl_models.o r_sprites.o r_modules.o r_explosion.o r_lerpanim.o protocol.o quakeio.o r_clip.o ui.o portals.o sys_shared.o cl_light.o gl_backend.o cl_particles.o cl_screen.o cgamevm.o cgame.o filematch.o collision.o cl_collision.o matrixlib.o
+OBJECTS= builddate.o $(CD) $(SND) chase.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_tent.o cmd.o common.o console.o crc.o cvar.o fractalnoise.o gl_draw.o r_sky.o gl_rmain.o gl_rsurf.o host.o host_cmd.o image.o keys.o mathlib.o menu.o model_alias.o model_brush.o model_shared.o model_sprite.o net_bsd.o net_udp.o net_dgrm.o net_loop.o net_main.o pr_cmds.o pr_edict.o pr_exec.o r_light.o r_explosion.o sbar.o sv_main.o sv_move.o sv_phys.o sv_user.o sv_light.o sys_linux.o transform.o view.o wad.o world.o zone.o vid_shared.o palette.o r_crosshairs.o gl_textures.o gl_models.o r_sprites.o r_modules.o r_explosion.o r_lerpanim.o protocol.o quakeio.o r_clip.o ui.o portals.o sys_shared.o cl_light.o gl_backend.o cl_particles.o cl_screen.o cgamevm.o cgame.o filematch.o collision.o cl_collision.o matrixlib.o cl_video.o dpvsimpledecode.o wavefile.o
 
 #K6/athlon optimizations
 CPUOPTIMIZATIONS=-march=k6
index 97ef1314479293c4944697b02a4e29997353860b..d7123629cb9067061980ce81d197131a28a35f2a 100644 (file)
@@ -1,5 +1,5 @@
 
-OBJECTS= builddate.o chase.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_tent.o cmd.o common.o console.o crc.o cvar.o fractalnoise.o gl_draw.o r_sky.o gl_rmain.o gl_rsurf.o host.o host_cmd.o image.o keys.o mathlib.o menu.o model_alias.o model_brush.o model_shared.o model_sprite.o net_dgrm.o net_loop.o net_main.o pr_cmds.o pr_edict.o pr_exec.o r_light.o r_explosion.o sbar.o snd_dma.o snd_mem.o snd_mix.o sv_main.o sv_move.o sv_phys.o sv_user.o sv_light.o transform.o view.o wad.o world.o zone.o vid_shared.o palette.o r_crosshairs.o gl_textures.o gl_models.o r_sprites.o r_modules.o r_explosion.o r_lerpanim.o protocol.o quakeio.o r_clip.o ui.o portals.o sys_shared.o cl_light.o gl_backend.o cl_particles.o cl_screen.o cgamevm.o cgame.o filematch.o collision.o cl_collision.o matrixlib.o
+OBJECTS= builddate.o chase.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_tent.o cmd.o common.o console.o crc.o cvar.o fractalnoise.o gl_draw.o r_sky.o gl_rmain.o gl_rsurf.o host.o host_cmd.o image.o keys.o mathlib.o menu.o model_alias.o model_brush.o model_shared.o model_sprite.o net_dgrm.o net_loop.o net_main.o pr_cmds.o pr_edict.o pr_exec.o r_light.o r_explosion.o sbar.o snd_dma.o snd_mem.o snd_mix.o sv_main.o sv_move.o sv_phys.o sv_user.o sv_light.o transform.o view.o wad.o world.o zone.o vid_shared.o palette.o r_crosshairs.o gl_textures.o gl_models.o r_sprites.o r_modules.o r_explosion.o r_lerpanim.o protocol.o quakeio.o r_clip.o ui.o portals.o sys_shared.o cl_light.o gl_backend.o cl_particles.o cl_screen.o cgamevm.o cgame.o filematch.o collision.o cl_collision.o matrixlib.o cl_video.o dpvsimpledecode.o wavefile.o
 
 #K6/athlon optimizations
 #CPUOPTIMIZATIONS=-march=k6
diff --git a/menu.c b/menu.c
index fbe9413d63990351765aaa1ea712094607715baa..3ab73593a3ae1f8d7a86124ba02466b33a8875b0 100644 (file)
--- a/menu.c
+++ b/menu.c
@@ -224,12 +224,6 @@ void M_BuildTranslationTable(int top, int bottom)
 }
 
 
-void M_DrawPicTranslate (float cx, float cy, char *picname)
-{
-       DrawQ_PicTranslate (menu_x + cx, menu_y + cy, picname, translationTable);
-}
-
-
 void M_DrawTextBox (float x, float y, float width, float height)
 {
        int n;
@@ -933,6 +927,28 @@ void M_Menu_Setup_f (void)
        setup_bottom = setup_oldbottom = cl_color.integer & 15;
 }
 
+// LordHavoc: rewrote this code greatly
+void M_MenuPlayerTranslate (qbyte *translation)
+{
+       int i, c;
+       unsigned int trans[4096];
+       qpic_t *p;
+
+       p = W_GetLumpName ("gfx/menuplyr.lmp");
+       if (!p)
+               return;
+       c = p->width * p->height;
+       if (c > 4096)
+       {
+               Con_Printf("M_MenuPlayerTranslate: image larger than 4096 pixel buffer\n");
+               return;
+       }
+
+       for (i = 0;i < c;i++)
+               trans[i] = d_8to24table[translation[((qbyte *)p->data)[i]]];
+
+       Draw_NewPic("gfx/menuplyr.lmp", p->width, p->height, true, (qbyte *)trans);
+}
 
 void M_Setup_Draw (void)
 {
@@ -957,8 +973,11 @@ void M_Setup_Draw (void)
        M_Print (72, 140, "Accept Changes");
 
        M_DrawPic (160, 64, "gfx/bigbox.lmp");
-       M_BuildTranslationTable(setup_top*16, setup_bottom*16);
-       M_DrawPicTranslate (172, 72, "gfx/menuplyr.lmp");
+
+       // LordHavoc: rewrote this code greatly
+       M_BuildTranslationTable (setup_top*16, setup_bottom*16);
+       M_MenuPlayerTranslate (translationTable);
+       M_DrawPic (172, 72, "gfx/menuplyr.lmp");
 
        M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1));
 
index 3abbe5839a951e47cb7b6de7cd00056ae394a011..d0646392f3e5c509359987ff2dd60c2fe58f8d8b 100644 (file)
@@ -53,6 +53,9 @@ rtexture_t *R_LoadTexture (rtexturepool_t *rtexturepool, char *identifier, int w
 // a procedurally generated texture, often animated over time, note: generate can be NULL (for odd uses)
 rtexture_t *R_ProceduralTexture (rtexturepool_t *rtexturepool, char *identifier, int width, int height, int textype, int flags, int (*generate)(qbyte *buffer, int width, int height, void *parameterdata, int parameterdatasize), void *parameterdata, int parameterdatasize);
 
+// free a texture
+void R_FreeTexture(rtexture_t *rt);
+
 // update the image data of a texture, used by lightmap updates and procedural
 // textures.
 void R_UpdateTexture(rtexture_t *rt, qbyte *data);
index 836227e00e07ad1c03cbbe6f0b9c75f47858ffbe..096fdfb603b61b9af9ffd7b71f2750e75b346836 100644 (file)
--- a/snd_dma.c
+++ b/snd_dma.c
@@ -1086,7 +1086,7 @@ void S_RawSamples_Dequeue(int *samples, unsigned int length)
        if (l < length)
        {
                memset(samples + l * 2, 0, (length - l) * sizeof(int[2]));
-               //Con_Printf("S_RawSamples_Dequeue: padding with %i\n", length - l);
+               //Con_Printf("S_RawSamples_Dequeue: padding with %i samples\n", length - l);
        }
        s_rawsamplesbuffer_start = (s_rawsamplesbuffer_start + l) % RAWSAMPLESBUFFER;
        s_rawsamplesbuffer_count -= l;
@@ -1098,3 +1098,40 @@ void S_RawSamples_ClearQueue(void)
        s_rawsamplesbuffer_start = 0;
 }
 
+int S_RawSamples_QueueWantsMore(void)
+{
+       if (s_rawsamplesbuffer_count < min(shm->speed >> 1, RAWSAMPLESBUFFER >> 1))
+               return RAWSAMPLESBUFFER - s_rawsamplesbuffer_count;
+       else
+               return 0;
+}
+
+void S_ResampleBuffer16Stereo(short *input, int inputlength, short *output, int outputlength)
+{
+       if (inputlength != outputlength)
+       {
+               int i, position, stopposition, step;
+               short *in, *out;
+               step = (float) inputlength * 256.0f / (float) outputlength;
+               position = 0;
+               stopposition = (inputlength - 1) << 8;
+               out = output;
+               for (i = 0;i < outputlength && position < stopposition;i++, position += step)
+               {
+                       in = input + ((position >> 8) << 1);
+                       out[0] = (((in[1] - in[0]) * (position & 255)) >> 8) + in[0];
+                       out[1] = (((in[3] - in[2]) * (position & 255)) >> 8) + in[2];
+                       out += 2;
+               }
+               stopposition = inputlength << 8;
+               for (i = 0;i < outputlength && position < stopposition;i++, position += step)
+               {
+                       in = input + ((position >> 8) << 1);
+                       out[0] = in[0];
+                       out[1] = in[2];
+                       out += 2;
+               }
+       }
+       else
+               memcpy(output, input, inputlength * sizeof(short[2]));
+}
diff --git a/sound.h b/sound.h
index 5ab1c4d8f020f8bcd5c0d832225a362b3f16943e..561272ba51d336203de9c7e0d1d6993f461c8dab 100644 (file)
--- a/sound.h
+++ b/sound.h
@@ -186,6 +186,11 @@ void S_RawSamples_Enqueue(short *samples, unsigned int length);
 void S_RawSamples_Dequeue(int *samples, unsigned int length);
 // empty the rawsamples queue
 void S_RawSamples_ClearQueue(void);
+// returns how much more data the queue wants, or 0 if it is already full enough
+int S_RawSamples_QueueWantsMore(void);
+
+// resamples one sound buffer into another, while changing the length
+void S_ResampleBuffer16Stereo(short *input, int inputlength, short *output, int outputlength);
 
 #endif
 
diff --git a/wavefile.c b/wavefile.c
new file mode 100644 (file)
index 0000000..eae62d6
--- /dev/null
@@ -0,0 +1,193 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "wavefile.h"
+
+wavefile_t *waveopen(char *filename, char **errorstring)
+{
+       int validfmt, position, length, l;
+       char *error;
+       wavefile_t *w;
+       FILE *file;
+       unsigned char buffer[1024];
+       error = NULL;
+       file = fopen(filename, "rb");
+       if (file)
+       {
+               w = malloc(sizeof(*w));
+               memset(w, 0, sizeof(*w));
+               if (w)
+               {
+                       w->file = file;
+                       if (fread(buffer, 12, 1, w->file))
+                       {
+                               if (!memcmp(buffer, "RIFF", 4))
+                               {
+                                       if (!memcmp(buffer + 8, "WAVE", 4))
+                                       {
+                                               validfmt = 0;
+                                               for(;;)
+                                               {
+                                                       if (!fread(buffer, 8, 1, w->file))
+                                                       {
+                                                               //error = "error reading chunk\n");
+                                                               break;
+                                                       }
+                                                       position = ftell(w->file);
+                                                       length = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
+                                                       if (!memcmp(buffer, "fmt ", 4))
+                                                       {
+                                                               validfmt = 0;
+                                                               l = length;
+                                                               if (l > 16)
+                                                                       l = 16;
+                                                               if (!fread(buffer, l, 1, w->file))
+                                                               {
+                                                                       error = "error reading \"fmt \" chunk\n";
+                                                                       break;
+                                                               }
+                                                               w->info_format = buffer[0] | (buffer[1] << 8);
+                                                               if (w->info_format != 1)
+                                                               {
+                                                                       error = "only PCM format supported\n";
+                                                                       break;
+                                                               }
+                                                               w->info_channels = buffer[2] | (buffer[3] << 8);
+                                                               if (w->info_channels != 1 && w->info_channels != 2)
+                                                               {
+                                                                       error = "only mono and stereo supported\n";
+                                                                       break;
+                                                               }
+                                                               w->info_rate = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
+                                                               if (w->info_rate < 1)
+                                                               {
+                                                                       error = "only rates 1hz-100khz supported\n";
+                                                                       break;
+                                                               }
+                                                               w->info_bits = buffer[14] | (buffer[15] << 8);
+                                                               if (w->info_bits != 8 && w->info_bits != 16)
+                                                               {
+                                                                       error = "only 8bit and 16bit supported\n";
+                                                                       break;
+                                                               }
+                                                               validfmt = 1;
+                                                       }
+                                                       else if (!memcmp(buffer, "data", 4))
+                                                       {
+                                                               if (validfmt)
+                                                               {
+                                                                       w->datalength = length;
+                                                                       w->dataposition = position;
+                                                               }
+                                                       }
+                                                       // other chunks that might be of interest:
+                                                       // "cue " (for looping)
+                                                       if (fseek(w->file, position + length, SEEK_SET))
+                                                       {
+                                                               error = "error seeking to next chunk\n";
+                                                               break;
+                                                       }
+                                               }
+                                               if (w->datalength && validfmt)
+                                               {
+                                                       w->info_bytesperchannel = w->info_bits / 8;
+                                                       w->info_bytespersample = w->info_channels * w->info_bytesperchannel;
+                                                       w->length = w->datalength / w->info_bytespersample;
+                                                       w->position = 0;
+                                                       fseek(w->file, w->dataposition, SEEK_SET);
+                                                       return w;
+                                               }
+                                       }
+                                       else
+                                               error = "not a RIFF WAVE file\n";
+                               }
+                               else
+                                       error = "not a RIFF file\n";
+                       }
+                       else
+                               error = "error reading file\n";
+                       free(w);
+               }
+               else
+                       error = "unable to allocate memory\n";
+               fclose(file);
+       }
+       else
+               error = "unable to open file\n";
+       if (errorstring)
+               *errorstring = error;
+       return NULL;
+}
+
+void waveclose(wavefile_t *f)
+{
+       if (f)
+       {
+               fclose(f->file);
+               free(f);
+       }
+}
+
+unsigned int waveread16stereo(wavefile_t *w, short *soundbuffer, unsigned int samples)
+{
+       int i;
+       int length;
+       unsigned char *in;
+       short *out;
+       length = samples;
+       if (length > w->length - w->position)
+               length = w->length - w->position;
+       if (w->bufferlength < length)
+       {
+               if (w->buffer)
+                       free(w->buffer);
+               w->bufferlength = length + 100;
+               w->buffer = malloc(w->bufferlength * w->info_bytespersample);
+       }
+       length = fread(w->buffer, w->info_bytespersample, length, w->file);
+       w->position += length;
+       if (length > 0)
+       {
+               if (w->info_bytesperchannel == 2)
+               {
+                       if (w->info_channels == 2)
+                       {
+                               for (i = 0, in = w->buffer, out = soundbuffer;i < length;i++, in += 4, out += 2)
+                               {
+                                       out[0] = in[0] | (in[1] << 8);
+                                       out[1] = in[2] | (in[3] << 8);
+                               }
+                       }
+                       else
+                               for (i = 0, in = w->buffer, out = soundbuffer;i < length;i++, in += 2, out += 2)
+                                       out[0] = out[1] = in[0] | (in[1] << 8);
+               }
+               else
+               {
+                       if (w->info_channels == 2)
+                       {
+                               for (i = 0, in = w->buffer, out = soundbuffer;i < length;i++, in += 2, out += 2)
+                               {
+                                       out[0] = (in[0] - 128) << 8;
+                                       out[1] = (in[1] - 128) << 8;
+                               }
+                       }
+                       else
+                               for (i = 0, in = w->buffer, out = soundbuffer;i < length;i++, in += 1, out += 2)
+                                       out[0] = out[1] = (in[0] - 128) << 8;
+               }
+       }
+       return length;
+}
+
+unsigned int waveseek(wavefile_t *w, unsigned int samples)
+{
+       if (samples > w->datalength)
+               return 1;
+       else
+       {
+               w->position = samples;
+               fseek(w->file, w->dataposition + w->position * w->info_bytespersample, SEEK_SET);
+               return 0;
+       }
+}
diff --git a/wavefile.h b/wavefile.h
new file mode 100644 (file)
index 0000000..de8b33a
--- /dev/null
@@ -0,0 +1,61 @@
+
+#ifndef WAVEFILE_H
+#define WAVEFILE_H
+
+typedef struct wavefile_s
+{
+       // file this is reading from
+       FILE *file;
+
+       // these settings are read directly from the wave format
+       // 1 is uncompressed PCM
+       unsigned int info_format;
+       // how many samples per second
+       unsigned int info_rate;
+       // how many channels (1 = mono, 2 = stereo, 6 = 5.1 audio?)
+       unsigned int info_channels;
+       // how many bits per channel (8 or 16)
+       unsigned int info_bits;
+
+       // these settings are generated from the wave format
+       // how many bytes in a sample (which may be one or two channels, thus 1 or 2 or 2 or 4, depending on info_bytesperchannel)
+       unsigned int info_bytespersample;
+       // how many bytes in channel (1 for 8bit, or 2 for 16bit)
+       unsigned int info_bytesperchannel;
+
+       // how many samples in the wave file
+       unsigned int length;
+
+       // how large the data chunk is
+       unsigned int datalength;
+       // beginning of data in data chunk
+       unsigned int dataposition;
+
+       // current position in stream (in samples)
+       unsigned int position;
+
+       // these are private to the wave file functions, just used for processing
+       // size of *buffer
+       unsigned int bufferlength;
+       // buffer is reallocated if caller asks for more than fits
+       void *buffer;
+
+}
+wavefile_t;
+
+// opens a wave file, if an error occurs and errorstring is not NULL,
+// *errorstring will be set to a message describing the error
+wavefile_t *waveopen(char *filename, char **errorstring);
+// closes a wave file
+void waveclose(wavefile_t *f);
+
+// reads some data from the file as 16bit stereo (converting if necessary)
+// returns number of samples read (may be less than requested)
+// if not all samples could be read, the remaining buffer will be unmodified
+unsigned int waveread16stereo(wavefile_t *f, short *soundbuffer, unsigned int samples);
+
+// seeks to a desired position in the wave
+// returns 0 if successful, 1 if not successful
+unsigned int waveseek(wavefile_t *f, unsigned int samples);
+
+#endif