aboutsummaryrefslogtreecommitdiff
path: root/Src/nsv
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/nsv
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/nsv')
-rw-r--r--Src/nsv/dec_if.h98
-rw-r--r--Src/nsv/enc_if.h44
-rw-r--r--Src/nsv/nsvbs.h202
-rw-r--r--Src/nsv/nsvencode.h221
-rw-r--r--Src/nsv/nsvlib.cpp737
-rw-r--r--Src/nsv/nsvlib.h364
-rw-r--r--Src/nsv/nsvplay/IDataReader.h21
-rw-r--r--Src/nsv/nsvplay/about.h661
-rw-r--r--Src/nsv/nsvplay/audiostub.cpp575
-rw-r--r--Src/nsv/nsvplay/audiostub.h8
-rw-r--r--Src/nsv/nsvplay/decoders.cpp675
-rw-r--r--Src/nsv/nsvplay/main.cpp230
-rw-r--r--Src/nsv/nsvplay/main.h248
-rw-r--r--Src/nsv/nsvplay/mp3stub.cpp73
-rw-r--r--Src/nsv/nsvplay/mp3stub.h12
-rw-r--r--Src/nsv/nsvplay/nsv_logo.bmpbin0 -> 7480 bytes
-rw-r--r--Src/nsv/nsvplay/nsvdecode.cpp999
-rw-r--r--Src/nsv/nsvplay/nsvplay.dsp464
-rw-r--r--Src/nsv/nsvplay/nsvplay.dsw29
-rw-r--r--Src/nsv/nsvplay/nsvplay.rc106
-rw-r--r--Src/nsv/nsvplay/readers.cpp949
-rw-r--r--Src/nsv/nsvplay/resource.h18
-rw-r--r--Src/nsv/nsvplay/subtitles.cpp215
-rw-r--r--Src/nsv/nsvplay/subtitles.h62
-rw-r--r--Src/nsv/nsvplay/vid_ddraw.cpp865
-rw-r--r--Src/nsv/nsvplay/vid_ddraw.h63
-rw-r--r--Src/nsv/nsvplay/vid_overlay.cpp654
-rw-r--r--Src/nsv/nsvplay/vid_overlay.h58
-rw-r--r--Src/nsv/nsvplay/video.cpp964
-rw-r--r--Src/nsv/nsvplay/video.h160
-rw-r--r--Src/nsv/nsvplay/vp3stub.cpp120
-rw-r--r--Src/nsv/nsvplay/vp3stub.h11
-rw-r--r--Src/nsv/nsvplay/vp5stub.cpp112
-rw-r--r--Src/nsv/nsvplay/vp5stub.h8
-rw-r--r--Src/nsv/nsvplay/wndmenu.h388
-rw-r--r--Src/nsv/svc_nsvFactory.cpp1
-rw-r--r--Src/nsv/svc_nsvFactory.h52
37 files changed, 10467 insertions, 0 deletions
diff --git a/Src/nsv/dec_if.h b/Src/nsv/dec_if.h
new file mode 100644
index 00000000..d0232327
--- /dev/null
+++ b/Src/nsv/dec_if.h
@@ -0,0 +1,98 @@
+/*
+** dec_if.h - common decoder interface for nsvplay/nsvplayx
+**
+** Copyright (C) 2001-2003 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty.
+** In no event will the authors be held liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial
+** applications, and to alter it and redistribute it freely, subject to the following restrictions:
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the
+** original software. If you use this software in a product, an acknowledgment in the product
+** documentation would be appreciated but is not required.
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as
+** being the original software.
+** 3. This notice may not be removed or altered from any source distribution.
+**
+*/
+
+#ifndef _NSV_DEC_IF_H_
+#define _NSV_DEC_IF_H_
+
+#ifndef _WA_IPC_H_ // these are also defined in Winamp/wa_ipc.h
+struct YV12_PLANE {
+ unsigned char* baseAddr;
+ long rowBytes;
+} ;
+
+struct YV12_PLANES {
+ YV12_PLANE y;
+ YV12_PLANE u;
+ YV12_PLANE v;
+} ;
+#endif
+
+class IVideoDecoder
+{
+ public:
+ virtual ~IVideoDecoder() { }
+
+ // decode returns 0 on success
+ // but *out can be NULL, meaning no new frame is available,
+ // but all is well
+ virtual int decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf)=0;
+ virtual void flush()=0;
+};
+
+class IAudioDecoder
+{
+ public:
+ virtual ~IAudioDecoder() { }
+
+ // returns -1 on error, 0 on success (done with data in 'in'), 1 on success
+ // but to pass 'in' again next time around.
+ virtual int decode(void *in, int in_len,
+ void *out, int *out_len, // out_len is read and written to
+ unsigned int out_fmt[8])=0; // out_fmt is written to
+ // ex: 'PCM ', srate, nch, bps
+ // or 'NONE' :)
+ virtual void flush()=0;
+};
+
+class IAudioOutput
+{
+ public:
+ virtual ~IAudioOutput() { }
+ virtual int canwrite()=0; // returns bytes writeable
+ virtual void write(void *buf, int len)=0;
+ virtual unsigned long long getpos()=0;
+ virtual unsigned long long getwritepos()=0;
+ virtual void flush(unsigned int newtime)=0;
+ virtual int isplaying(void) { return 1; }
+ virtual void pause(int pause) { }
+ virtual void setvolume(int volume) { }
+ virtual void setpan(int pan) { }
+ virtual void getdescstr(char *buf) { *buf=0; }
+};
+
+
+/*
+** The DLL must export one of these symbols (unmangled, i.e. using extern "C).
+**
+** IAudioDecoder *CreateAudioDecoder(unsigned int type, IAudioOutput **output);
+** IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int type, int *flip);
+**
+** the functions should return NULL if the conversion is not possible (or
+** is not the correct format)
+**
+** The DLL must be in <program files>\common files\nsv, and must be named
+** nsvdec_*.dll.
+**
+*/
+
+#endif//_NSV_DEC_IF_H_
diff --git a/Src/nsv/enc_if.h b/Src/nsv/enc_if.h
new file mode 100644
index 00000000..65daf32f
--- /dev/null
+++ b/Src/nsv/enc_if.h
@@ -0,0 +1,44 @@
+/*
+** enc_if.h - common encoder interface
+**
+** Copyright (C) 2001-2003 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty.
+** In no event will the authors be held liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial
+** applications, and to alter it and redistribute it freely, subject to the following restrictions:
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the
+** original software. If you use this software in a product, an acknowledgment in the product
+** documentation would be appreciated but is not required.
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as
+** being the original software.
+** 3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef _NSV_ENC_IF_H_
+#define _NSV_ENC_IF_H_
+class VideoCoder
+{
+ public:
+ VideoCoder() { }
+ virtual int Encode(void *in, void *out, int *iskf)=0; // returns bytes in out
+ virtual ~VideoCoder() { };
+};
+
+class AudioCoder
+{
+ public:
+ AudioCoder() { }
+ virtual int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail)=0; //returns bytes in out
+ virtual ~AudioCoder() { };
+};
+
+// unsigned int GetAudioTypes3(int idx, char *desc);
+// unsigned int GetVideoTypes3(int idx, char *desc);
+// AudioCoder *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile);
+// VideoCoder *CreateVideo3(int w, int h, double frt, unsigned int pixt, unsigned int *outt, char *configfile);
+// HWND ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile);
+// HWND ConfigVideo3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile);
+
+#endif //_NSV_ENC_IF_H_
diff --git a/Src/nsv/nsvbs.h b/Src/nsv/nsvbs.h
new file mode 100644
index 00000000..9ec59ef1
--- /dev/null
+++ b/Src/nsv/nsvbs.h
@@ -0,0 +1,202 @@
+/*
+** nsvbs.h - NSV basic inline bitstream classes
+**
+** Copyright (C) 2001-2002 Nullsoft, Inc.
+** Confidential Subject to NDA
+**
+** Note: these bitstream classes encode/decode everything in LSB.
+** bits are stored from the lowest bit to the highest bit.
+** putbits(4,0xE) will result in getbits(1)=0, getbits(1)=1,
+** getbits(1)=1, getbits(1)=1
+** or of course, getbits(4) == 0xE :)
+*/
+
+#ifndef _NSVBS_H_
+#define _NSVBS_H_
+
+#include <stdlib.h>
+#include <memory.h>
+#include <bfc/platform/types.h>
+#include "../nu/GrowBuf.h"
+
+class nsv_OutBS
+{
+public:
+ nsv_OutBS() { m_used = 0; m_curb=0; }
+ ~nsv_OutBS() { }
+
+ void putbits(int nbits, unsigned int value)
+ {
+ while (nbits-- > 0)
+ {
+ m_curb|=(value&1)<<(m_used&7);
+ if (!((++m_used)&7))
+ {
+ m_bits.add(&m_curb,1);
+ m_curb=0;
+ }
+ value>>=1;
+ }
+ }
+
+ // lets you put in any amount of data, but does not preserve endianness.
+ void putdata(int nbits, void *data)
+ {
+ unsigned char *c=(unsigned char *)data;
+ if (!(m_used&7) && nbits >= 8)
+ {
+ m_bits.add(c,nbits/8);
+ c+=nbits/8;
+ m_used+=nbits&~7;
+ nbits&=7;
+ }
+ while (nbits > 0)
+ {
+ int tb=nbits;
+ if (tb > 8) tb=8;
+ putbits(tb,*c++);
+ nbits-=tb;
+ }
+ }
+
+ int getlen() { return m_used; } // in bits
+
+ void *get(int *len) // len is in bytes, forces to byte aligned.
+ {
+ if (m_used&7)
+ {
+ m_bits.add(&m_curb,1);
+ m_used=(m_used+7)&~7;
+ m_curb=0;
+ }
+ *len=m_used/8;
+ return m_bits.get();
+ }
+
+ void clear()
+ {
+ m_used=0;
+ m_curb=0;
+ m_bits.resize(0);
+ }
+
+private:
+ GrowBuf m_bits;
+ int m_used; // bits
+ unsigned char m_curb;
+};
+
+class nsv_InBS {
+public:
+ nsv_InBS() { m_bitpos=0; m_eof=0; }
+ ~nsv_InBS() { }
+
+ void clear()
+ {
+ m_eof=0;
+ m_bitpos=0;
+ m_bits.resize(0);
+ }
+
+ void add(void *data, int len)
+ {
+ m_bits.add(data,len);
+ m_eof=0;
+ }
+
+ void addbyte(unsigned char byte)
+ {
+ add(&byte,1);
+ }
+
+ void addint(unsigned int dword)
+ {
+ addbyte(dword&0xff);
+ addbyte((dword>>8)&0xff);
+ addbyte((dword>>16)&0xff);
+ addbyte((dword>>24)&0xff);
+ }
+
+ void compact()
+ {
+ size_t bytepos=m_bitpos/8;
+ if (bytepos)
+ {
+ unsigned char *t=(unsigned char *)m_bits.get();
+ size_t l=m_bits.getlen()-bytepos;
+ memcpy(t,t+bytepos,l);
+ m_bits.resize(l);
+ m_bitpos&=7;
+ }
+ m_eof=0;
+ }
+
+ void seek(ptrdiff_t nbits)
+ {
+ if (nbits < 0 && ((size_t)(-nbits)) > m_bitpos)
+ m_bitpos=0;
+ else
+ m_bitpos+=nbits;
+ m_eof=m_bits.getlen()*8 < m_bitpos;
+ }
+
+ void rewind() { m_bitpos=0; m_eof=0; }
+ int eof() { return m_eof; }
+ size_t avail() { if (m_eof) return 0; return m_bits.getlen()*8 - m_bitpos; }
+
+ unsigned int getbits(size_t nbits)
+ {
+ unsigned int ret=0;
+ if (!nbits) return ret;
+ unsigned char *t=(unsigned char *)m_bits.get();
+
+ if (!t || m_bits.getlen()*8 < m_bitpos+nbits) m_eof=1;
+ else
+ {
+ t+=m_bitpos/8;
+ for (size_t sh = 0; sh != nbits; sh ++)
+ {
+ ret|=((*t>>(m_bitpos&7))&1) << sh;
+ if (!((++m_bitpos)&7)) t++;
+ }
+ }
+ return ret;
+ }
+
+ int getdata(size_t nbits, void *data)
+ {
+ unsigned char *t=(unsigned char *)data;
+ if (m_bits.getlen()*8 < m_bitpos+nbits) return 1;
+ if (!(m_bitpos&7) && nbits >= 8)
+ {
+ char *bitptr=(char*)m_bits.get();
+ bitptr+=(m_bitpos/8);
+ memcpy(t,bitptr,nbits/8);
+ m_bitpos+=nbits&~7;
+
+ t+=nbits/8;
+ nbits&=7;
+ }
+ while (nbits > 0)
+ {
+ size_t nb=nbits;
+ if (nb > 8) nb=8;
+ *t++=getbits(nb);
+ nbits-=nb;
+ }
+ return 0;
+ }
+
+ void *getcurbyteptr()
+ {
+ char *t=(char*)m_bits.get();
+ return (void *)(t+(m_bitpos/8));
+ }
+
+private:
+ GrowBuf m_bits;
+ size_t m_bitpos;
+ int m_eof;
+};
+
+#endif//_NSVBS_H_ \ No newline at end of file
diff --git a/Src/nsv/nsvencode.h b/Src/nsv/nsvencode.h
new file mode 100644
index 00000000..39f1f907
--- /dev/null
+++ b/Src/nsv/nsvencode.h
@@ -0,0 +1,221 @@
+#ifndef _NSVENCODE_H_
+#define _NSVENCODE_H_
+
+#include "../jnetlib/jnetlib.h"
+#include "nsvlib.h"
+#include "enc_if.h"
+#include <stdio.h>
+
+class nsv_Encoder
+{
+ public:
+ nsv_Encoder();
+ ~nsv_Encoder();
+
+
+ static void freeCoders(); // call this before quitting if you used any nsvencoders
+ static void loadCoders();
+ static HWND configAudio(unsigned int audfmt, HWND hwndParent, char *configfile);
+ static HWND configVideo(unsigned int vidfmt, HWND hwndParent, char *configfile);
+ static int enumAudio(int *state, char *name, unsigned int *fmt);
+ static int enumVideo(int *state, char *name, unsigned int *fmt);
+
+
+ char *getError() { return m_err; } // NULL on no error
+
+ // compressor configuration (do not call these after calling openOutput())
+ void setVideo(unsigned int vfmt, unsigned int w, unsigned int h, double frt,
+ unsigned int srcfmt=NSV_MAKETYPE('R','G','B','A'),
+ char *configfile=NULL);
+ void setAudio(unsigned int afmt, int srate, int nch, int bps,
+ unsigned int srcfmt=NSV_MAKETYPE('P','C','M',' '),
+ char *configfile=NULL);
+
+ // stream configuration (do not call these after calling openOutput())
+ void setMaxAudioSendAhead(int offs) { m_audio_ahead_of_video=offs; }
+ void setSyncFrameInterval(int minimum=0, int maximum=120) { m_max_syncframe_dist=maximum; m_min_syncframe_dist=minimum; }
+
+ // setting one of these will enable file header writing on files
+ // do NOT call these after calling openOutput(),
+ int addHdrMetaData(char *name, char *data, int data_len); // returns nonzero on error
+ int setHdrMetaData(char *data, int data_len); // nonzero on error
+ void setHdrTOCSize(int tocsize);
+ void forceHdrWrite() { m_usefilehdr=1; } // use if you dont want meta or toc, but want a basic header
+
+ // output (call only once, do not close and reopen (yet))
+ void openOutput(char *url); // call getError to check for error
+ void closeOutput();
+
+ // encode (call only after openoutput())
+ void addAudioData(void *data, int data_len);
+ int needAudioData(); // returns 1 if more audio data would be nice for sync, 2 if there is no audio data in buffer
+
+ // for audio only, use compressor type NONE and call this with NULL
+ void addVideoFrame(void *frame);
+ // calling this will put the aux data in the same frame as the NEXT addVideoFrame()
+ void addAuxData(unsigned int fmt, void *data, int data_len);
+
+ // setting audio eof will let the audio compressor know that it is the end of stream
+ // so that it can flush (via passing an empty frame to it)
+ void set_audioEof(int eof) { m_audio_eof=!!eof; }
+ int get_audioEof() { return m_audio_eof; }
+
+ // stats
+ unsigned int getCurrentBitrate()
+ {
+ if (m_video_position_frames)
+ return (unsigned int) ((double) m_bitstreambytesout / ((double) m_video_position_frames / m_framerate) * 8.0);
+ return 0;
+ }
+
+
+ private:
+
+ char *m_err;
+
+ int m_usefilehdr;
+ unsigned int m_tocsize;
+ int m_audio_channels;
+ int m_audio_samplerate;
+ int m_audio_bits;
+ unsigned int m_audio_srcfmt;
+ unsigned int m_audio_coder;
+ char *m_audio_configfile;
+
+
+ double m_framerate;
+ unsigned int m_w, m_h;
+ unsigned int m_video_coder;
+ unsigned int m_video_srcfmt;
+ char *m_video_configfile;
+
+ int m_audio_ahead_of_video;
+
+ int m_max_syncframe_dist,m_min_syncframe_dist;
+ nsv_fileHeader m_filehdr;
+
+ FILE *m_outfile;
+ JNL_Connection *m_outcon;
+ int m_is_uvox; // for m_outco
+ char *m_outcon_host,*m_outcon_headers;
+ int m_outcon_port;
+ unsigned int m_outcon_last_connect_time;
+
+ nsv_Packeter m_packeter;
+ nsv_InBS m_outconbs;
+ __int64 m_bitstreambytesout;
+
+ AudioCoder *m_audcoder;
+ VideoCoder *m_vidcoder;
+
+ __int64 m_audio_position_bytes;
+ __int64 m_video_position_frames;
+ char m_vidobuf[NSV_MAX_VIDEO_LEN];
+ char m_audobuf[NSV_MAX_AUDIO_LEN];
+ nsv_InBS m_audibuf;
+ unsigned int m_total_frames;
+ nsv_GrowBuf m_tocEntries;
+ int m_frames_since_last_sync_frame;
+ int m_audio_eof;
+
+ void send_uvoxmessage(int msgclass, int msgtype, void *msg, int msglen);
+
+
+ static VideoCoder *init_video(int w, int h, double frt, unsigned int pixt, unsigned int *outt, char *configfile);
+ static AudioCoder *init_audio(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile);
+ static int Coders_loaded;
+ static HINSTANCE g_Coders[256];
+ static int g_numCoders;
+ static char g_dll_dir[MAX_PATH];
+ static BOOL CALLBACK PcmProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
+
+
+
+};
+
+
+
+class nsv_EncoderConfigFile
+{
+ public:
+
+ nsv_EncoderConfigFile(char *filename=NULL)
+ {
+ m_filename=filename?_strdup(filename):0;
+ m_audfmt=m_vidfmt=0;
+ m_minsync=0;
+ m_maxsync=120;
+ m_audiosendahead=0;
+ m_hdrwrite=0;
+ m_hdrtoc=1024;
+ m_metadata=0;
+ m_crop_l=0; // ##
+ m_crop_r=0; // ##
+ m_crop_t=0; // ##
+ m_crop_b=0; // ##
+
+ ReadIn();
+ }
+
+
+ ~nsv_EncoderConfigFile(); //ak 7-23-03 moved so breakpoint can be set
+ unsigned int getVidFmt() { return m_vidfmt; }
+ unsigned int getAudFmt() { return m_audfmt; }
+ void setVidFmt(unsigned int fmt) { m_vidfmt=fmt; }
+ void setAudFmt(unsigned int fmt) { m_audfmt=fmt; }
+
+ char *getFilename() { return m_filename; }
+ void setFilename(char *filename)
+ {
+ FlushOut();
+ free(m_filename);
+ if (filename)
+ {
+ m_filename=_strdup(filename);
+ ReadIn();
+ }
+ else m_filename=0;
+ }
+
+ int getMinSyncFrameInt() { return m_minsync; }
+ void setMinSyncFrameInt(int minint) { m_minsync=minint; }
+ int getMaxSyncFrameInt() { return m_maxsync; }
+ void setMaxSyncFrameInt(int maxint) { m_maxsync=maxint; }
+ int getAudioSendAhead() { return m_audiosendahead; }
+ void setAudioSendAhead(int sa) { m_audiosendahead=sa; }
+
+ char *hdrGetMetadata() { return m_metadata; }
+ int hdrGetDoWrite() { return m_hdrwrite; }
+ int hdrSetDoWrite(int dw) { m_hdrwrite=!!dw; }
+ int hdrGetTOCSize() { return m_hdrtoc; }
+ int hdrSetTOCSize(int ts) { m_hdrtoc=ts; }
+
+ int ConfigUI(HINSTANCE hInstance, HWND hwndParent);
+
+ private:
+
+ void FlushOut();
+ void ReadIn();
+
+ static BOOL CALLBACK _dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
+ BOOL dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
+ HWND audio_hwnd, video_hwnd;
+ void initConfigChildWindows(HWND hwndDlg, int flags);
+
+ char *m_filename;
+
+ unsigned int m_audfmt,m_vidfmt;
+ int m_minsync, m_maxsync;
+ int m_audiosendahead;
+ int m_hdrwrite;
+ int m_hdrtoc;
+ char *m_metadata;
+
+ public:
+ int m_crop_l; // ##
+ int m_crop_r; // ##
+ int m_crop_t; // ##
+ int m_crop_b; // ##
+};
+
+#endif//_NSVENCODE_H_ \ No newline at end of file
diff --git a/Src/nsv/nsvlib.cpp b/Src/nsv/nsvlib.cpp
new file mode 100644
index 00000000..e26bef2f
--- /dev/null
+++ b/Src/nsv/nsvlib.cpp
@@ -0,0 +1,737 @@
+/*
+** 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;
+}
diff --git a/Src/nsv/nsvlib.h b/Src/nsv/nsvlib.h
new file mode 100644
index 00000000..b773d996
--- /dev/null
+++ b/Src/nsv/nsvlib.h
@@ -0,0 +1,364 @@
+/*
+** nsvlib.h - NSV file/bitstream reading/writing interface
+**
+** Copyright (C) 2001-2002 Nullsoft, Inc.
+**
+** Confidential Subject to NDA
+*/
+
+#ifndef _NSVLIB_H_
+#define _NSVLIB_H_
+
+/*********************************************************************
+** bitstream classes
+*/
+
+#include "nsvbs.h"
+
+
+/*********************************************************************
+** NSV packeting limits
+*/
+#define NSV_MAX_AUDIO_LEN 0x8000 // 32kb
+#define NSV_MAX_VIDEO_LEN 0x80000 // 512kb
+#define NSV_MAX_AUX_LEN 0x8000 // 32kb for each aux stream
+#define NSV_MAX_AUXSTREAMS 15 // 15 aux streams maximum
+
+
+/*********************************************************************
+** Constants for setting certain metadata items using addHdrMetaData()
+*/
+#define METADATANAME_AUTHOR "Author"
+#define METADATANAME_TITLE "Title"
+#define METADATANAME_COPYRIGHT "Copyright"
+#define METADATANAME_COMMENT "Comment"
+#define METADATANAME_PROFILE "Profile"
+#define METADATANAME_FILEID "File ID"
+
+/*********************************************************************
+** NSV type utility functions/macros
+*/
+
+/*
+** Use NSV_MAKETYPE() to quickly make NSV audio/video/aux types.
+** ex: NSV_MAKETYPE('R','G','B','A')
+*/
+#define NSV_MAKETYPE(A,B,C,D) ((A) | ((B)<<8) | ((C)<<16) | ((D)<<24))
+
+/*
+** These functions convert types to and from strings.
+*/
+
+/* nsv_type_to_string() converts an NSV type to a string.
+ * out must be at least 5 bytes long. If 't' is not a valid type,
+ * then out will be set to an empty string
+ * ex:
+ * char out[5];
+ * nsv_type_to_string(NSV_MAKETYPE('R','G','B','A'),out);
+ * strcmp(out,"RGBA") == 0
+ */
+void nsv_type_to_string(unsigned int t, char *out);
+
+/* nsv_string_to_type() converts a string to an NSV type.
+ * Returns 0 if the type is not valid.
+ * ex: nsv_string_to_type("RGBA") == NSV_MAKETYPE('R','G','B','A')
+ */
+unsigned int nsv_string_to_type(char *in);
+
+
+/*********************************************************************
+** NSV bitstream packeting/unpacketing classes
+*/
+
+
+/* nsv_Packeter is used to packet audio/video/auxiliary data into
+ * a bitstream.
+ *
+ * ex:
+ * nsv_Packeter p;
+ * nsv_OutBS bs;
+ * p.setVidFmt(NSV_MAKETYPE('R','G','B','A'),320,240,30.0);
+ * p.setAudFmt(NSV_MAKETYPE('P','C','M',' '));
+ * for (;;) {
+ * doEncodeAudioAndVideo();
+ * p.setSyncFrame(is_keyframe);
+ * p.setSyncOffset(av_sync_offset);
+ * p.setAudio(audio_data,audio_len);
+ * p.setVideo(video_data,video_len);
+ * p.clearAuxChannels(); // you can add aux channels if you want
+ * if (p.packet(bs)) error();
+ * int outbuflen;
+ * void *outbuf=bs.get(&outbuflen);
+ * fwrite(outbuf,outbuflen,1,fp); // write output
+ * bs.clear(); // clear bitstream
+ * }
+ *
+ */
+
+class nsv_Packeter {
+ public:
+ nsv_Packeter();
+ ~nsv_Packeter();
+
+ // init (per file) calls
+ void setVidFmt(unsigned int vfmt, unsigned int w, unsigned int h, double frt);
+ void setAudFmt(unsigned int afmt) { audfmt=afmt; }
+
+ // per frame calls
+ void setSyncFrame(int is_syncframe) { is_sync_frame=is_syncframe; }
+ void setSyncOffset(int syncoffs) { syncoffset_cur=syncoffs; }
+ void setAudio(void *a, int a_len) { audio=a; audio_len=a_len; }
+ void setVideo(void *v, int v_len) { video=v; video_len=v_len; }
+ int addAuxChannel(unsigned int fmt, void *data, int data_len) // 0 on success
+ {
+ if (aux_used >= NSV_MAX_AUXSTREAMS) return -1;
+ aux[aux_used]=data;
+ aux_len[aux_used]=data_len;
+ aux_types[aux_used]=fmt;
+ aux_used++;
+ return 0;
+ }
+ void clearAuxChannels() { aux_used=0; }
+
+ int packet(nsv_OutBS &bs); // returns 0 on success
+
+ // some utility getting functions
+ unsigned int getAudFmt() { return audfmt; }
+ unsigned int getVidFmt() { return vidfmt; }
+ unsigned int getWidth() { return width; }
+ unsigned int getHeight() { return height; }
+ double getFrameRate() { return framerate; }
+
+ private:
+ unsigned char framerate_idx;
+ unsigned int vidfmt;
+ unsigned int audfmt;
+ unsigned int width;
+ unsigned int height;
+ double framerate;
+ int syncoffset_cur;
+
+ int aux_used;
+ void *aux[NSV_MAX_AUXSTREAMS];
+ int aux_len[NSV_MAX_AUXSTREAMS];
+ unsigned int aux_types[NSV_MAX_AUXSTREAMS];
+ int is_sync_frame;
+ void *audio;
+ int audio_len;
+ void *video;
+ int video_len;
+};
+
+
+/* nsv_Unpacketer is used to unpacket a bitstream into audio/video/auxiliary data
+ * to decode, use an nsv_InBS object with data, and call unpacket().
+ * ex:
+ * nsv_Unpacketer up;
+ * nsv_InBS in;
+ * nsv_InBS videoout, audioout;
+ * up.setVideoOut(&videoout);
+ * up.setAudioOut(&audioout);
+ * for (;;) {
+ * int ret=up.unpacket(in);
+ * if (ret < 0) break; // eof
+ * if (ret > 0) add_data_to_bitstream(&in,ret);
+ * if (!ret) { // got frame
+ * int vl=videoout.getbits(32);
+ * int al=videoout.getbits(32);
+ * char *vd=(char*)videoout.getcurbyteptr();
+ * char *ad=(char*)audioout.getcurbyteptr();
+ * doDecode(vd,vl,ad,al);
+ * videoout.seek(vl*8);
+ * audioout.seek(al*8);
+ * videoout.compact(); // free memory up
+ * audioout.compact(); // free memory up
+ * in.compact(); // free memory up
+ * }
+ * }
+ */
+
+class nsv_Unpacketer {
+ public:
+ nsv_Unpacketer() { reset(); }
+ ~nsv_Unpacketer() { }
+
+ void reset(int full=1); // if full, full reset is done.
+ // if not, then it is a partial reset (ie for seeking)
+
+ // when EOF is set, the unpacketer will fail instead of requesting more data at the
+ // end; it will also not require that the next frame be available for sync
+ // (normally it looks ahead to verify data)
+ void setEof(int eof=1) { m_eof=eof; }
+ int getEof() { return m_eof; }
+
+ // use these to set where the unpacketer writes the output of each stream
+ // set to NULL to ignore output of that stream
+
+ void setAudioOut(nsv_InBS *output=NULL) { m_audiobs=output; }
+ // the format of the audio data written to the output is:
+ // 32 bits: length of frame
+ // ? bytes: audio data
+ // (to read):
+ // int l=output->getbits(32);
+ // decode_audio(output->getcurbyteptr(),l);
+ // output->seek(l*8);
+
+
+ void setVideoOut(nsv_InBS *output=NULL) { m_videobs=output; }
+ // the format of the video data written to the output is:
+ // 32 bits: length of frame
+ // ? bytes: video data
+ // (to read):
+ // int l=output->getbits(32);
+ // decode_video(output->getcurbyteptr(),l);
+ // output->seek(l*8);
+
+ void setAuxOut(nsv_InBS *output=NULL) { m_auxbs=output; }
+ // the format of the aux data written to the output is:
+ // 32 bits: length of frame
+ // 32 bits: type of aux data
+ // ? bytes: aux data
+ // (to read):
+ // int l=output->getbits(32);
+ // int type=output->getbits(32);
+ // decode_aux(output->getcurbyteptr(),l);
+ // output->seek(l*8);
+ // aux is different than audio/video in that it includes a 32 bit
+ // type value that is not included in the length.
+
+
+ // returns 0 on success, >0 on needs (at least X bytes) more data,
+ // -1 on error (eof and no header found)
+ int unpacket(nsv_InBS &bs);
+
+
+ // do we have enough sync to determine formats/widths/heights/framerates
+ int isValid() { return valid; }
+
+ // are we fully synched?
+ int isSynched() { return synched; }
+
+ // get sync offset from when we first synched up
+ signed int getSyncOffset() { return (signed int) syncoffset; }
+
+ // get sync offset from current frame (not usually used)
+ signed int getCurSyncOffset() { return (signed int) syncoffset_cur; }
+
+ // get video, audio, width, height, framerate formats.
+ unsigned int getVidFmt() { return vidfmt; }
+ unsigned int getAudFmt() { return audfmt; }
+ unsigned int getWidth() { return width; }
+ unsigned int getHeight() { return height; }
+ double getFrameRate() { return framerate; }
+ unsigned char getFrameRateIdx() { return framerate_idx; }
+
+ // is current frame a sync frame?
+ int isSynchFrame() { return is_sync_frame; }
+
+ private:
+ nsv_InBS *m_audiobs, *m_videobs, *m_auxbs;
+ int valid; // contents of stream info are valid for syncing
+ int synched; // turns off anal packet checking
+ unsigned int vidfmt;
+ unsigned int audfmt;
+ unsigned int width;
+ unsigned int height;
+ double framerate;
+ int is_sync_frame;
+ unsigned char framerate_idx;
+ int syncoffset;
+ int syncoffset_cur;
+
+ int m_eof;
+};
+
+
+/*********************************************************************
+** NSV file header reading/writing functions
+*/
+
+
+typedef struct {
+ // header_size is the size of NSV header. nsv_writeheader() and nsv_readheader()
+ // will set this automatically
+ unsigned int header_size;
+
+ // file_lenbytes is the size of the NSV bitstream (not including the header size)
+ // this can be 0xFFFFFFFF to signify unknown length
+ unsigned int file_lenbytes;
+
+ // file_lenms is the length of the NSV bitstream in milliseconds.
+ // this can be 0xFFFFFFFF to signify unknown length
+ unsigned int file_lenms;
+
+ // metadata_len describes the length of the metadata.
+ unsigned int metadata_len;
+
+ // toc_alloc describes the allocated length of the TOC (in entries).
+ // set this to zero to use toc_size (recommended).
+ unsigned int toc_alloc;
+
+ // toc_size describes the used size of the TOC (in entries)
+ // set this to zero to disable the TOC. When using toc_ex,
+ // this must be < toc_alloc/2 (if using nsv_writeheader, and
+ // toc_size is too big, toc_alloc will be grown automatically.
+ unsigned int toc_size;
+
+ // buffer which contains the TOC. this will be automatically
+ // allocated when using nsv_readheader(), but you should allocate
+ // this yourself when using nsv_writeheader()
+ unsigned int *toc;
+
+ // if used, contains time pairs (in frames) for the offset. this will be
+ // automatically allocated when using nsv_readheader(), but you should allocate
+ // this yourself when using nsv_writeheader()
+ // DO NOT FREE THIS VALUE IF IT WAS ALLOCATED FROM NSV_READHEADER. :)
+ // (it is just an extension of toc, which should be freed with free())
+ unsigned int *toc_ex;
+
+ // buffer which contains metadata. allocated when using nsv_readheader(),
+ // but you should allocate this yourself when using nsv_writeheader()
+ // note that nsv_readheader() will NULL terminate this buffer.
+ void *metadata;
+
+} nsv_fileHeader;
+
+// nsv_writeheader() writes the NSV file header to the bitstream bs.
+// the NSV file header will be at LEAST padto bytes long (usually
+// you will leave padto to 0)
+void nsv_writeheader(nsv_OutBS &bs, nsv_fileHeader *hdr, unsigned int padto);
+
+// nsv_readheader() reads an NSV file header from a bitstream bs.
+// if the return value is less than zero, then there is no NSV
+// file header in bs. if the return value is zero, the NSV file
+// header was succesfully read. if the return value is positive,
+// then at least that many more bytes are needed to decode the
+// header.
+// ex:
+// nsv_InBS bs;
+// nsv_fileHeader hdr;
+// for (;;) {
+// int ret=nsv_readheader(bs,&hdr);
+// if (ret<=0) break;
+// addBytesToBs(bs,ret);
+// }
+// if (hdr.header_size) { we_got_valid_header(&hdr); }
+//
+
+int nsv_readheader(nsv_InBS &bs, nsv_fileHeader *hdr);
+
+
+// nsv_getmetadata() retrieves a metadata item from the metadata
+// block. if that item is not found, NULL is returned.
+// Note that the value returned by nsv_getmetadata() has been
+// malloc()'d, and you must free() it when you are done.
+// ex:
+// char *v=nsv_getmetadata(hdr.metadata,"TITLE");
+// if (v) printf("title=%s\n",v);
+// free(v);
+//
+
+char *nsv_getmetadata(void *metadata, char *name);
+
+
+#endif//_NSVLIB_H_
diff --git a/Src/nsv/nsvplay/IDataReader.h b/Src/nsv/nsvplay/IDataReader.h
new file mode 100644
index 00000000..1ae234bf
--- /dev/null
+++ b/Src/nsv/nsvplay/IDataReader.h
@@ -0,0 +1,21 @@
+#ifndef NULLSOFT_NSV_NSVPLAY_IDATAREADER_H
+#define NULLSOFT_NSV_NSVPLAY_IDATAREADER_H
+
+#include <stddef.h>
+#include <bfc/platform/types.h>
+
+class IDataReader
+{
+public:
+ virtual ~IDataReader() { }
+ virtual size_t read(char *buf, size_t len)=0; // returns bytes read
+ virtual bool iseof()=0;
+ virtual char *geterror()=0;
+ virtual char *gettitle() { return 0; }
+ virtual char *getheader(char *header_name) { return 0; }
+ virtual bool canseek() { return 0; }
+ virtual int seek(uint64_t newpos) { return 1; }
+ virtual uint64_t getsize() { return ~0; }
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nsv/nsvplay/about.h b/Src/nsv/nsvplay/about.h
new file mode 100644
index 00000000..a04e48d6
--- /dev/null
+++ b/Src/nsv/nsvplay/about.h
@@ -0,0 +1,661 @@
+#ifndef _ABOUT_H_
+#define _ABOUT_H_
+
+#include <math.h>
+
+
+#pragma warning(disable : 4731)
+
+static HWND about_hwnd;
+
+#ifndef NO_ABOUT_EGG
+
+#define BITMAP_W 100
+#define BITMAP_H 64
+static HDC m_hdc;
+static HBITMAP m_hbm;
+static char *m_dib;
+static char ge_fbuf[(BITMAP_H+1)*BITMAP_W+1];
+static int ge_tmp;
+#define M_NUM_FX 9
+static int m_effect;
+#define BLOBS_NPOINTS 8
+static int BLOBPOINTS[4*BLOBS_NPOINTS];
+static int fire_textcnt,fire_textpos;
+static char *fire_texts[]={
+ "nsvplay",
+ "Nullsoft 2003-8",
+ "",
+ "greets to",
+ "winamp forums",
+ "#nullsoft",
+ "britney spears",
+ "p.s.",
+ "DrO was here",
+ " <3",
+ "",
+};
+static HDC scrolldc;
+static HBITMAP scrollbitmap;
+static char *scrolldib;
+static const char scrolltext[]="nullsoft presents you nSVpLAY hidden part! cracked by rOn +5 trainer by deadbeef ";
+static int scrolloffs;
+static char *rototmp;
+
+#endif //NO_ABOUT_EGG
+
+static INT_PTR CALLBACK aboutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+#ifndef NO_ABOUT_EGG
+ {
+ HBITMAP about_bmp = NULL;
+ m_effect=M_NUM_FX-1;
+
+ // try to use the localised image and then revert to the normal dll image
+ about_bmp = (HBITMAP)LoadImage((HINSTANCE)lParam,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_SHARED);
+ if(about_bmp == NULL){
+ about_bmp = (HBITMAP)LoadImage(g_hInstance,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_SHARED);
+ }
+
+ // set on control with id of -1 (0xFFFFFFFF) or 0xFFFF (not sure how/why this happened)
+ SendDlgItemMessage(hwndDlg,0xFFFF,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)about_bmp);
+ SendDlgItemMessage(hwndDlg,-1,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)about_bmp);
+ }
+#endif
+ SetDlgItemText(hwndDlg,IDC_VERSION,WNDMENU_CAPTION);
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDCANCEL:
+ case IDOK:
+#ifndef MODAL_ABOUT
+ DestroyWindow(hwndDlg);
+#else
+ EndDialog(hwndDlg,1);
+#endif
+ return FALSE;
+ }
+ break;
+
+ case WM_DESTROY:
+ about_hwnd=NULL;
+#ifndef NO_ABOUT_EGG
+ if (m_hbm) DeleteObject(m_hbm);
+ if (m_hdc) DeleteDC(m_hdc);
+ if (scrollbitmap) DeleteObject(scrollbitmap);
+ if (scrolldc) DeleteDC(scrolldc);
+ m_hbm=0;
+ m_hdc=0;
+ scrollbitmap=0;
+ scrolldc=0;
+ if (rototmp) free(rototmp);
+ rototmp=NULL;
+#endif
+ break;
+#ifndef NO_ABOUT_EGG
+ case WM_LBUTTONDBLCLK :
+ //easter eggs :)
+ if (++m_effect >= M_NUM_FX) m_effect=1;
+
+ KillTimer(hwndDlg,0x1234);
+
+ if (m_hbm) DeleteObject(m_hbm);
+ if (m_hdc) DeleteDC(m_hdc);
+ if (scrollbitmap) DeleteObject(scrollbitmap);
+ if (scrolldc) DeleteDC(scrolldc);
+ scrollbitmap=0;
+ scrolldc=0;
+ if (rototmp) free(rototmp);
+ rototmp=NULL;
+
+ {
+ struct
+ {
+ BITMAPINFO bmi;
+ RGBQUAD more_bmiColors[256];
+ LPVOID data;
+ } m_bitmap;
+ m_hdc = CreateCompatibleDC(NULL);
+ m_bitmap.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ m_bitmap.bmi.bmiHeader.biPlanes = 1;
+ m_bitmap.bmi.bmiHeader.biBitCount = 8;
+ m_bitmap.bmi.bmiHeader.biCompression = BI_RGB;
+ m_bitmap.bmi.bmiHeader.biSizeImage = 0;
+ m_bitmap.bmi.bmiHeader.biClrUsed = 256;
+ m_bitmap.bmi.bmiHeader.biClrImportant = 256;
+ m_bitmap.bmi.bmiHeader.biWidth = BITMAP_W;
+ m_bitmap.bmi.bmiHeader.biHeight = -BITMAP_H;
+ m_bitmap.bmi.bmiHeader.biSizeImage = BITMAP_W*BITMAP_H;
+
+ memset(ge_fbuf,0,BITMAP_W*(BITMAP_H+1));
+ fire_textcnt=0;
+
+ if (m_effect < 3)
+ {
+ unsigned char *t=(unsigned char *)m_bitmap.bmi.bmiColors;
+ int x=255;
+ int adj=!!m_effect;
+ t[0]=t[1]=t[2]=0;
+ t+=4;
+ while (x)
+ {
+ if (m_effect == 2)
+ {
+ if (x > 128)
+ {
+ t[0]=0;
+ t[1]=((256-x)*2)/3;
+ t[2]=(256-x)*2;
+ }
+ else
+ {
+ t[0]=256-x*2;
+ t[1]=255/3 + ((256-x)*2)/3;
+ t[2]=255;
+ }
+ }
+ else
+ {
+ int a=x*2;
+ if (a>255) a=255;
+ t[0]=a;
+ t[2-adj]=a;
+ a+=a;
+ if (a > 255) a=255;
+ t[1+adj]=a;
+ }
+
+ t+=4;
+ x--;
+ }
+ }
+
+ if(m_effect==0)
+ {
+ //sine scroll
+ struct
+ {
+ BITMAPINFO bmi;
+ RGBQUAD more_bmiColors[1];
+ LPVOID data;
+ } m_bitmap;
+ scrolldc = CreateCompatibleDC(NULL);
+ m_bitmap.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ m_bitmap.bmi.bmiHeader.biPlanes = 1;
+ m_bitmap.bmi.bmiHeader.biBitCount = 8;
+ m_bitmap.bmi.bmiHeader.biCompression = BI_RGB;
+ m_bitmap.bmi.bmiHeader.biSizeImage = 0;
+ m_bitmap.bmi.bmiHeader.biClrUsed = 1;
+ m_bitmap.bmi.bmiHeader.biClrImportant = 1;
+ m_bitmap.bmi.bmiHeader.biWidth = 128;
+ m_bitmap.bmi.bmiHeader.biHeight = -32;
+ m_bitmap.bmi.bmiHeader.biSizeImage = 128*32;
+ scrollbitmap = CreateDIBSection(scrolldc,&m_bitmap.bmi,DIB_RGB_COLORS, &m_bitmap.data, NULL, 0);
+ SelectObject(scrolldc,scrollbitmap);
+ SetBkMode(scrolldc,TRANSPARENT);
+ scrolldib = (char *)m_bitmap.data;
+ scrolloffs = 0;
+ }
+
+ if (m_effect == 1)
+ {
+ int *t=BLOBPOINTS;
+ int a=BLOBS_NPOINTS;
+ while (a)
+ {
+ t[0]=(rand()&127) - 64;
+ t[2]=(rand()&127) - 64;
+ t[1]=t[3]=a;
+ t+=4;
+ a--;
+ }
+ }
+
+ if((m_effect >= 3 && m_effect <= 8)) //rotozooms
+ {
+ rototmp=(char *)malloc(65536+256);
+ //generate noise texture
+ memset(rototmp,0xd0,65536+256);
+ __asm
+ {
+ mov cx,0ffffh
+ xor ax,ax
+ xor ebx,ebx
+ xor dx,dx
+ mov edi,[rototmp]
+ TEXGEN:
+ mov bx,cx
+ add ax,cx
+ rol ax,cl
+ mov dh,al
+ sar dh,5
+ adc dl,dh
+ adc dl,[edi+ebx+255]
+ shr dl,1
+ mov [edi+ebx],dl
+ not bh
+ mov [edi+ebx],dl
+ loop TEXGEN
+ }
+
+ if ((!!(GetAsyncKeyState(VK_SHIFT)&0x8000)) ^ (m_effect==5)) // secondary easter egg, hah!
+ for (int x = 0; x < 256*256; x ++) rototmp[x] = 0x40 + ((x^(x>>8)) & 0x1F);
+
+ rototmp[0]=rototmp[1];
+ rototmp[0xff00]=rototmp[0xff01];
+
+ //generate palette
+ unsigned char *t=((unsigned char *)(&m_bitmap.bmi.bmiColors[0x40]));
+ for(int i=0;i<0x20;i++)
+ {
+ int r,g,b;
+ switch(m_effect)
+ {
+ case 3: r=i*4; g=i*5; b=i*8; break;
+ case 4: r=i*4; g=i*7; b=i*8; break;
+ case 5: r=i*8; g=i*2; b=i*2; break;
+ case 6: r=i*6; g=i*8; b=i*6; break;
+ case 7: r=i*8; g=i*6; b=i*8; break;
+ case 8: r=i*6; g=i*6; b=i*8; break;
+ }
+ t[0]=b;
+ t[1]=g;
+ t[2]=r;
+ t+=4;
+ }
+ }
+
+ m_hbm = CreateDIBSection(m_hdc,&m_bitmap.bmi,DIB_RGB_COLORS, &m_bitmap.data, NULL, 0);
+ SelectObject(m_hdc,m_hbm);
+ m_dib = (char *)m_bitmap.data;
+
+ SetTimer(hwndDlg,0x1234,35,NULL);
+ }
+ break;
+
+ case WM_TIMER:
+ {
+ int nomemcpy=0;
+ static float inc=0;
+ inc++;
+ if (m_effect == 0) // oldsk00l sine scroll
+ {
+ double blah=3.14/BITMAP_W;
+ double val1=1;
+ double val2=(BITMAP_H-16)/2;
+ static double vinc=0;
+ __asm
+ {
+ mov edi, offset ge_fbuf
+ mov ecx, BITMAP_W*BITMAP_H
+ xor eax, eax
+ rep stosb
+ }
+
+ for(int j=0;j<8;j++) {
+ for(int k=0;k<16;k++) {
+ int col=255-k*(256/16);
+ if(col<128) col=128+(128-col);
+ memset(ge_fbuf+(k+(int)((BITMAP_H-16)*(cos(vinc+(j*8)*3.14/50)+1)/2))*BITMAP_W,col,BITMAP_W);
+ }
+ }
+
+ __asm
+ {
+ mov edi, offset ge_fbuf
+ mov ecx, 0
+ mov esi, dword ptr [scrolldib]
+
+ SINELOOP:
+ mov [ge_tmp], ecx
+ fild dword ptr [ge_tmp]
+ fmul qword ptr [blah]
+ fadd qword ptr [vinc]
+ fcos
+ fadd qword ptr [val1]
+ fmul qword ptr [val2]
+ fistp dword ptr [ge_tmp]
+ mov eax, [ge_tmp]
+ mov ebx, eax
+
+ mov edx, BITMAP_W
+ mul edx
+
+ mov dh, bl
+ push ecx
+ mov ebx, 0
+ mov ecx, 16
+
+ SINELOOP2:
+ cmp byte ptr [esi+ebx],0
+ je SINECONT
+
+ mov dl,0ffh
+ sub dl,dh
+ sub dl,cl
+
+ mov [edi+eax], dl
+ SINECONT:
+ add eax, BITMAP_W
+ add ebx, 128
+ loop SINELOOP2
+
+ pop ecx
+
+ inc esi
+ inc edi
+
+ inc ecx
+ cmp ecx, BITMAP_W
+ jl SINELOOP
+
+ mov edi, [scrolldib]
+ mov esi, edi
+ inc esi
+ mov ebx, 32
+ SINESCROLL:
+ mov ecx, 127
+ rep movsb
+ inc edi
+ inc esi
+ dec ebx
+ jnz SINESCROLL
+ }
+ vinc+=0.2;
+ scrolloffs++;
+ if((scrolloffs&7)==7)
+ {
+ int o=scrolloffs/8;
+ if(!scrolltext[o]) scrolloffs=0;
+ else TextOutA(scrolldc,100,0,&scrolltext[o],1);
+ }
+ }
+ else if (m_effect == 1) // blobs
+ {
+ int *blobptr=BLOBPOINTS;
+ int i=BLOBS_NPOINTS*2;
+ while (i--)
+ {
+ if (blobptr[0] > 0) blobptr[1]--;
+ else blobptr[1]++;
+
+ int a=blobptr[1];
+ if (rand()&1) a++;
+ else a--;
+ blobptr[0]+=a/8;
+
+ blobptr+=2;
+ }
+
+ int y=BITMAP_H;
+ unsigned char *p=(unsigned char *)ge_fbuf;
+ while (y--)
+ {
+ int x=BITMAP_W;
+ while (x--)
+ {
+ blobptr=BLOBPOINTS;
+ i=BLOBS_NPOINTS;
+ double sum=0.0;
+ while (i--)
+ {
+ double as=(x-(BITMAP_W/2)) - blobptr[0];
+ double bs=(y-(BITMAP_H/2)) - blobptr[2];
+ sum+=sqrt(as*as + bs*bs);
+ blobptr+=4;
+ }
+ sum *= 6.0/BLOBS_NPOINTS;
+ int a=(int)sum;
+ if (a > 0xff) a= 0xff;
+ *p++=a;
+ }
+ }
+ }
+ else if(m_effect==2) //gayfire
+ {
+ unsigned char *p=(unsigned char *)ge_fbuf;
+ int x;
+ unsigned char *t=p + BITMAP_W*BITMAP_H;
+ for (x = 0; x < BITMAP_W; x ++)
+ {
+ int a=*t - 10;
+ if ((rand()&0x7) == 7) a+=100;
+ if (a < 0) a=0;
+ else if (a > 192) a=192;
+ *t++=a;//rand()&0xf0;
+ }
+ int y;
+ for (y = 0; y < BITMAP_H; y ++)
+ {
+ *p++=p[0]/4 + p[BITMAP_W]/2 + p[BITMAP_W+1]/4;
+
+ for (x = 1; x < BITMAP_W-1; x ++)
+ *p++=p[0]/4 + p[BITMAP_W]/4 + p[BITMAP_W-1]/4 + p[BITMAP_W+1]/4;
+
+ *p++=p[0]/4 + p[BITMAP_W]/2 + p[BITMAP_W-1]/4;
+ }
+ if (fire_textcnt-- <= 0)
+ {
+ memcpy(m_dib,ge_fbuf,BITMAP_W*BITMAP_H);
+ SetBkMode(m_hdc,TRANSPARENT);
+ SetTextColor(m_hdc,RGB(255,255,255));
+ RECT r={0,0,BITMAP_W,BITMAP_H};
+ DrawTextA(m_hdc,fire_texts[fire_textpos%(sizeof(fire_texts)/sizeof(fire_texts[0]))],-1,&r,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
+ if (fire_textcnt < -30)
+ {
+ memcpy(ge_fbuf,m_dib,BITMAP_W*BITMAP_H);
+ fire_textpos++;
+ fire_textcnt=30;
+ }
+ else nomemcpy=1;
+ }
+ }
+ else if (m_effect == 3) //rotozoom
+ {
+ char *p=ge_fbuf;
+ static float angle=0;
+ for(int j=-32;j<32;j++)
+ for(int i=-50;i<50;i++)
+ {
+ //rotozoom
+ double x=(i*cosf(angle)-j*sinf(angle))*(2+cosf(angle*1.4f));
+ double y=(i*sinf(angle)+j*cosf(angle))*(2+cosf(angle*1.4f));
+ //slime
+ x+=cos(angle*x)*4;
+ y+=sin(angle*y)*4;
+ int x2=(int)x & 0xff;
+ int y2=(int)y & 0xff;
+ *p++=rototmp[256*y2+x2];
+ }
+
+ angle+=0.01f;
+ }
+ else if (m_effect == 4) //rotozoom 2
+ {
+ char *p=ge_fbuf;
+ double angle=cos(inc*0.01f)*2;
+ for(int j=-32;j<32;j++)
+ for(int i=-50;i<50;i++)
+ {
+ //position
+ double x=i-cos(inc*0.013f)*64;
+ double y=j+sin(inc*0.013f)*64;
+
+ //slime
+ x+=cos((angle+i)*50)*cos(angle)*4;
+ y+=sin((angle+j)*50)*cos(angle)*4;
+
+ //rotozoom
+ double x3=(x*cos(angle)-y*sin(angle))*(2+cos(angle*1.4f));
+ double y3=(x*sin(angle)+y*cos(angle))*(2+cos(angle*1.4f));
+
+ int x2=(int)x3 & 0xff;
+ int y2=(int)y3 & 0xff;
+ *p++=rototmp[256*y2+x2];
+ }
+ }
+ else if (m_effect == 5) //3d rotozoom
+ {
+ char *p=ge_fbuf;
+ static float angle=0;
+ const double b=50;
+ for(int j=-32;j<32;j++)
+ for(int i=-50;i<50;i++)
+ {
+ //rotozoom
+ double x=(i*cos(angle)+j*sin(angle));//*(2+cos(angle*1.4f));
+ double y=(i*sin(angle)-j*cos(angle));//*(2+cos(angle*1.4f));
+ //gay z-projection
+ x*=b/(((double)j+32));
+ y*=b/(((double)j+32));
+ //position
+ x-=cos(inc*0.013f)*64;
+ y+=sin(inc*0.013f)*64;
+
+ int x2=(int)x & 0xff;
+ int y2=(int)y & 0xff;
+
+ char c=rototmp[256*y2+x2];
+ *p++=0x40+((c-0x40)*(j+32)/56);
+ }
+
+ angle+=0.01f;
+ //b++;
+ }
+ else if (m_effect == 6) //tunnel
+ {
+ const double TINYNUM=1.0E-6;
+ const double CONE_RADIUS=128;
+ const double FOV=120.0;
+ #define sqr(a) ((a)*(a))
+
+ char *p=ge_fbuf;
+ for(int y=-32;y<32;y++)
+ for(int x=-50;x<50;x++)
+ {
+ double originx=cos(inc*0.025f)*20;
+ double originy=sin(inc*0.04f)*20;
+ double originz=inc*4;
+ double dirx=x/FOV;
+ double diry=y/FOV;
+ double dirz=1;
+
+ //normalize dir vector
+ {
+ double l=1.0f/sqrt(sqr(dirx)+sqr(diry)+sqr(dirz));
+ dirx*=l;
+ diry*=l;
+ dirz*=l;
+ }
+
+ //y-axis rotation
+ {
+ double rot=inc*0.015f;
+ double dirx2=dirx*cos(rot)+dirz*sin(rot);
+ dirz=dirx*sin(rot)-dirz*cos(rot);
+ dirx=dirx2;
+ }
+
+ //tunnel algo shit
+ double a=sqr(dirx)+sqr(diry);
+ double b=2*(originx*dirx + originy*diry);
+ double c=sqr(originx)+sqr(originy)-sqr(CONE_RADIUS);
+ double delta=sqrt(sqr(b)-(4*a*c));
+
+ double t1=(-b+delta)/(2*a+TINYNUM);
+ double t2=(-b-delta)/(2*a+TINYNUM);
+
+ double t=t1>0?t1:t2;
+
+ double intx=originx+dirx*t;
+ double inty=originy+diry*t;
+ double intz=originz+dirz*t;
+
+ //tex. coords
+ int u=(int)(fabs(intz)*0.6);
+ int v=(int)(fabs(atan2(inty,intx)*256/3.14159265));
+
+ //depth
+ t=20000.0/t;
+ int z=(int)(t>63?63:t);
+
+ u&=0xff;
+ v&=0xff;
+ z&=0xff;
+
+ {
+ char c=rototmp[256*u+v];
+ *p++=0x40+((c-0x40)*z/64);
+ }
+ }
+ }
+ else if(m_effect==7) //washing machine
+ {
+ char *p=ge_fbuf;
+ for(int j=-32;j<32;j++)
+ for(int i=-50;i<50;i++)
+ {
+ double dist=sqrt(double(sqr(i)+sqr(j))); // pythagoras rules :)
+ double angle=cos(dist*0.05f)*(cos(inc*0.1f)) + inc*0.07f;
+ //rotozoom
+ double x=(i*cos(angle)-j*sin(angle));
+ double y=(i*sin(angle)+j*cos(angle));
+ int x2=(int)x & 0xff;
+ int y2=(int)y & 0xff;
+ *p++=rototmp[256*y2+x2];
+ }
+ }
+ else if(m_effect==8) //reflection-like(?) effect
+ {
+ char *p=ge_fbuf;
+ for(int j=-32;j<32;j++)
+ for(int i=-50;i<50;i++)
+ {
+ double dist=sqrt(double(sqr(i)+sqr(j)));
+ double zoom=cos(dist*0.05f)*(cos(inc*0.02f)*8)+1;
+ //rotozoom
+ double x=i*zoom+inc;
+ double y=j*zoom+inc;
+ int x2=(int)x & 0xff;
+ int y2=(int)y & 0xff;
+ *p++=rototmp[256*x2+y2];
+ }
+ }
+
+ if (!nomemcpy) memcpy(m_dib,ge_fbuf,BITMAP_W*BITMAP_H);
+ if (hwndDlg != NULL)
+ {
+ HDC h = GetDC(hwndDlg);
+ BitBlt(h, 11, 11, BITMAP_W, BITMAP_H, m_hdc, 0, 0, SRCCOPY);
+ ReleaseDC(hwndDlg, h);
+ }
+ }
+ break;
+#endif
+ }
+ return 0;
+}
+
+static void do_about(HWND hwnd, HINSTANCE hinst) {
+#ifndef MODAL_ABOUT
+ if(about_hwnd) {
+ SetForegroundWindow(about_hwnd);
+ return;
+ }
+ about_hwnd=CreateDialogParam((!hinst?g_hInstance:hinst),MAKEINTRESOURCE(IDD_ABOUT),hwnd,aboutProc,(LPARAM)hinst);
+ ShowWindow(about_hwnd,SW_SHOW);
+#else
+#ifdef LOC_MODAL_ABOUT
+ WASABI_API_DIALOGBOXPARAMW(IDD_ABOUT,hwnd,aboutProc,(LPARAM)hinst);
+#else
+ DialogBoxParam((!hinst?g_hInstance:hinst),MAKEINTRESOURCE(IDD_ABOUT),hwnd,aboutProc,(LPARAM)hinst);
+#endif
+#endif
+}
+
+#endif//_ABOUT_H_ \ No newline at end of file
diff --git a/Src/nsv/nsvplay/audiostub.cpp b/Src/nsv/nsvplay/audiostub.cpp
new file mode 100644
index 00000000..83794693
--- /dev/null
+++ b/Src/nsv/nsvplay/audiostub.cpp
@@ -0,0 +1,575 @@
+#include <windows.h>
+#include "audiostub.h"
+
+#define CAPTION "NSV Player Sound Output Error"
+#define MAX(x,y) (( y ) < ( x ) ? ( x ) : ( y ))
+#define MIN(x,y) (( x ) < ( y ) ? ( x ) : ( y ))
+
+#define S_MINSIZE (1<<28)
+#define MAX_NUM_BLOCKS 8
+
+#define NUM_BLOCKS 8
+#define BUFSIZE_MS 1500
+
+#define BLOCKSIZE_MAX 32768
+#define BLOCKSIZE_MIN 8192
+
+int g_audio_use_mixer=0;
+
+class PCM_AudioOut : public IAudioOutput
+{
+ public:
+ PCM_AudioOut(int samplerate, int numchannels, int bitspersamp);
+ ~PCM_AudioOut();
+
+ int canwrite(); // returns bytes writeable
+ void write(void *_buf, int len);
+ unsigned int getpos();
+ unsigned int getwritepos();
+ void flush(unsigned int time_ms);
+ void pause(int pause);
+ int isplaying(void);
+ void setvolume(int volume);
+ void setpan(int pan);
+
+ int open_success() { return init; }
+ void getdescstr(char *buf)
+ {
+ *buf=0;
+ if (g_srate && g_nch) wsprintf(buf,"%dkHz %s",g_srate/1000,g_nch==2?"stereo":"mono");
+ }
+
+ private:
+ DWORD ThreadP();
+
+ void _setvol(void);
+
+ static DWORD WINAPI _threadproc(LPVOID p);
+ static void CALLBACK cbFunc(HWAVEOUT hwo,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2)
+ {
+ if (uMsg == WOM_DONE) { ReleaseSemaphore((HANDLE)dwInstance,1,NULL);}
+ }
+ void do_set_blocksizes(void);
+ void do_samples_altvol(char *in, int blen);
+
+ int init;
+
+ int min_blocksize;
+ int max_blocksize;
+
+ int ispcnt;
+ int num_blocks;
+
+ char *g_buffer, *g_buffer_write, *g_buffer_read;
+ int g_buffer_length, g_buffer_valid,g_buffer_inlength;
+ HWAVEOUT g_hWaveOut;
+ WAVEHDR g_wave_headers[MAX_NUM_BLOCKS];
+ int g_bps,g_nch,g_srate;
+ volatile int g_pause, g_wavecnt, g_prebuf,g_writeall, g_quit_flag,g_writetime_bytes, g_outtime_bytes, g_outtime_interp;
+
+ HANDLE g_hSem,g_hEvent, g_hThread;
+ CRITICAL_SECTION g_cs;
+
+ int g_bytes_per_sec;
+ int a_v,a_p;
+
+ unsigned char *g_vol_table;
+};
+
+
+PCM_AudioOut::PCM_AudioOut(int samplerate, int numchannels, int bitspersamp)
+{
+ init=0;
+ a_v=255;
+ a_p=0;
+ num_blocks=0;
+
+ g_buffer_valid=g_buffer_inlength=0;
+ g_hWaveOut=NULL;
+ memset(g_wave_headers,0,sizeof(g_wave_headers));
+
+ g_hSem=g_hEvent=g_hThread=0;
+
+ int x;
+ DWORD id;
+ MMRESULT res;
+ WAVEFORMATEX wfx={WAVE_FORMAT_PCM,numchannels,samplerate,samplerate*numchannels*(bitspersamp/8),
+ numchannels*(bitspersamp/8),bitspersamp};
+
+ g_bps=bitspersamp;
+ g_nch=numchannels;
+ g_srate=samplerate;
+ g_bytes_per_sec=wfx.nAvgBytesPerSec;
+
+ {
+ char *p=(char*)g_wave_headers;
+ int n=sizeof(g_wave_headers);
+ while (n--) *p++=0;
+ }
+ g_buffer_length = MulDiv(g_bytes_per_sec, BUFSIZE_MS, 1000);
+ g_buffer_length &= ~1023;
+ if (g_buffer_length < 4096) g_buffer_length=4096;
+
+ g_buffer=(char *)GlobalAlloc(GMEM_FIXED,g_buffer_length+min(65536,g_buffer_length));
+ if (g_buffer == NULL)
+ {
+ MessageBox(NULL,"Error allocating buffer", CAPTION,MB_OK|MB_ICONSTOP);
+ return;
+ }
+
+ g_prebuf=g_buffer_length/4;
+ g_buffer_read=g_buffer_write=g_buffer;
+ g_wavecnt=g_pause=g_writeall=g_buffer_valid=g_writetime_bytes=g_outtime_bytes=g_buffer_inlength=0;
+ g_quit_flag=0;
+
+ g_vol_table=NULL;
+
+ do_set_blocksizes();
+
+ g_hSem=CreateSemaphore(NULL,0,256,NULL);
+ for (x = 0; (res=waveOutOpen(&g_hWaveOut,WAVE_MAPPER,&wfx,(DWORD)cbFunc,(DWORD)g_hSem,CALLBACK_FUNCTION))==MMSYSERR_ALLOCATED && x<10; x ++)
+ Sleep(100);
+ if (res != MMSYSERR_NOERROR)
+ {
+ char t[512];
+ waveOutGetErrorText(res,t,sizeof(t));
+ MessageBox(NULL,t, CAPTION,MB_OK|MB_ICONSTOP);
+ GlobalFree((HGLOBAL) g_buffer);
+ CloseHandle(g_hSem);
+ g_buffer = NULL;
+ return;
+ }
+
+ ispcnt=0;
+ g_outtime_interp=GetTickCount();
+ g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
+ InitializeCriticalSection(&g_cs);
+
+ g_hThread=CreateThread(NULL,0,_threadproc,(void*)this,0,&id);
+ SetThreadPriority(g_hThread,THREAD_PRIORITY_HIGHEST);
+
+ init=1;
+}
+
+
+void PCM_AudioOut::do_set_blocksizes(void)
+{
+ int t,t2,t4;
+ t=(MulDiv(BLOCKSIZE_MIN,g_bytes_per_sec,44100)+1023)&~1023;
+ if (t<1024) t=1024;
+ if (t>32768) t=32768;
+
+ t2=(MulDiv(BLOCKSIZE_MAX,g_bytes_per_sec,44100*4)+1023)&~1023;
+ if (t2 < t) t2 = t;
+ if (t2 > 65536) t2=65536;
+
+ t4 = NUM_BLOCKS;
+
+ num_blocks=t4;
+ max_blocksize=t2;
+ min_blocksize=t;
+}
+
+
+PCM_AudioOut::~PCM_AudioOut(void)
+{
+ if (init)
+ {
+ int x;
+ g_quit_flag=1;
+ SetEvent(g_hEvent);
+ while (g_quit_flag == 1) Sleep(70);
+
+ waveOutReset(g_hWaveOut);
+
+ for (x = 0; x < MAX_NUM_BLOCKS; x++)
+ if (g_wave_headers[x].dwFlags & WHDR_PREPARED)
+ waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
+ if (waveOutClose(g_hWaveOut) != MMSYSERR_NOERROR)
+ {
+ MessageBox(NULL,"Error closing sound device.",CAPTION,MB_OK);
+ }
+ if (g_buffer) GlobalFree((HGLOBAL) g_buffer);
+ DeleteCriticalSection(&g_cs);
+ CloseHandle(g_hThread);
+ CloseHandle(g_hSem);
+ CloseHandle(g_hEvent);
+
+ if(g_vol_table) GlobalFree((HGLOBAL)g_vol_table);
+ }
+}
+
+void PCM_AudioOut::write(void *_buf, int len)
+{
+ char *buf=(char *)_buf;
+ int l2;
+ if (len > 8192) len=8192;
+ l2=(g_buffer_write+len)-(g_buffer+g_buffer_length);
+ if (len <= 0 || !buf)
+ {
+ g_writeall=1;
+ //g_prebuf=0;
+ SetEvent(g_hEvent);
+ return;
+ }
+ ispcnt=0;
+ g_writeall=0;
+ if (l2 > 0)
+ {
+ int l1=len-l2;
+ memcpy(g_buffer_write,buf,l1);
+ memcpy(g_buffer,buf+l1,l2);
+ g_buffer_write=g_buffer+l2;
+ }
+ else
+ {
+ memcpy(g_buffer_write,buf,len);
+ g_buffer_write += len;
+ if (g_buffer_write == g_buffer+g_buffer_length) g_buffer_write=g_buffer;
+ }
+
+ EnterCriticalSection(&g_cs);
+ g_buffer_valid+=len;
+ LeaveCriticalSection(&g_cs);
+ g_writetime_bytes+=len;
+ if (g_wavecnt < num_blocks)
+ {
+ SetEvent(g_hEvent);
+ }
+ return;
+}
+
+int PCM_AudioOut::canwrite(void)
+{
+ int t=(g_buffer_length-g_buffer_valid);
+ if (g_wavecnt==0)
+ {
+ SetEvent(g_hEvent);
+ }
+ if (t>8192) t=8192; // RG: since write() caps the # of bytes at 8192, this should reflect that! otherwise, if we call write() with too many bytes, it throws the extra away and we'll never know it.
+ return t;
+}
+
+int PCM_AudioOut::isplaying(void)
+{
+ if (g_wavecnt==0)
+ {
+ SetEvent(g_hEvent);
+ }
+ if (ispcnt < 7) ispcnt++;
+ if (g_buffer_valid < MIN(g_buffer_length/2,min_blocksize) && ispcnt==7)
+ {
+ g_writeall=1;
+ g_prebuf=0;
+ }
+ return (g_wavecnt>0) || (g_buffer_valid>0);
+}
+
+void PCM_AudioOut::pause(int pause)
+{
+ if (g_hWaveOut)
+ {
+ int lastp=g_pause;
+ g_pause=pause;
+ if (pause == lastp) return;
+ if (g_pause)
+ waveOutPause(g_hWaveOut);
+ else
+ {
+ waveOutRestart(g_hWaveOut);
+ g_outtime_interp=GetTickCount();
+ SetEvent(g_hEvent);
+ }
+ }
+}
+
+void PCM_AudioOut::_setvol(void)
+{
+ DWORD vol, vo2, gv;
+ if (g_hWaveOut)
+ {
+ a_v=MIN(255,MAX(a_v,0));
+ a_p=MIN(127,MAX(a_p,-127));
+
+ vo2 = vol = (a_v*65535) / 255;
+
+ if (a_p > 0)
+ {
+ vol *= (127-a_p);
+ vol /= 127;
+ }
+ else if (a_p < 0)
+ {
+ vo2 *= (127+a_p);
+ vo2 /= 127;
+ }
+ gv=vol|(vo2<<16);
+ if(g_audio_use_mixer) {
+ if(g_vol_table) {
+ EnterCriticalSection(&g_cs);
+ GlobalFree((HGLOBAL)g_vol_table);
+ g_vol_table=NULL;
+ LeaveCriticalSection(&g_cs);
+ }
+ waveOutSetVolume(g_hWaveOut,gv);
+ } else {
+ EnterCriticalSection(&g_cs);
+ if(!g_vol_table) {
+ int l=(g_bps==8)?512:(4*32769);
+ g_vol_table=(unsigned char *)GlobalAlloc(GPTR,l);
+ }
+ //compute volume lookup table
+ int x;
+ if (g_bps==8) {
+ if (g_nch==1) for (x = 0; x < 256; x ++) g_vol_table[x] = (x*a_v)/256;
+ else for (x = 0; x < 256; x ++)
+ {
+ g_vol_table[x] = (x*(int)vol)/65536;
+ g_vol_table[x+256] = (x*(int)vo2)/65536;
+ }
+ } else {
+ short *vol_tab16 = (short *)g_vol_table;
+ if (g_nch==1) for (x = 0; x < 32769; x ++) vol_tab16[x] = -(x*a_v)/256;
+ else for (x = 0; x < 32769; x ++)
+ {
+ vol_tab16[x] = -(x*(int)vol)/65536;
+ vol_tab16[x+32769] = -(x*(int)vo2)/65536;
+ }
+ }
+ LeaveCriticalSection(&g_cs);
+ }
+ }
+}
+
+
+void PCM_AudioOut::flush(unsigned int time_ms)
+{
+ int x;
+ EnterCriticalSection(&g_cs);
+ g_outtime_bytes=g_writetime_bytes=MulDiv(time_ms,g_bytes_per_sec,1000);
+ waveOutReset(g_hWaveOut);
+ for (x = 0; x < MAX_NUM_BLOCKS; x++)
+ if ((g_wave_headers[x].dwFlags & WHDR_PREPARED))
+ {
+ waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
+ g_wave_headers[x].dwFlags =0;
+ }
+ while (WaitForSingleObject(g_hSem,0)==WAIT_OBJECT_0);
+ g_prebuf=g_buffer_length/8;
+ g_buffer_read=g_buffer_write=g_buffer;
+ g_writeall=g_wavecnt=g_buffer_valid=g_buffer_inlength=0;
+ ispcnt=0;
+ LeaveCriticalSection(&g_cs);
+}
+
+unsigned int PCM_AudioOut::getwritepos(void)
+{
+ return MulDiv(g_writetime_bytes,1000,g_bytes_per_sec);
+}
+
+unsigned int PCM_AudioOut::getpos(void)
+{
+ unsigned int t;
+ if (!g_pause)
+ {
+ t=GetTickCount()-g_outtime_interp;
+ if (t > 1000) t=1000;
+ }
+ else t=0;
+ return t+MulDiv(g_outtime_bytes,1000,g_bytes_per_sec);
+}
+
+DWORD WINAPI PCM_AudioOut::_threadproc(LPVOID p)
+{
+ return ((PCM_AudioOut *)p)->ThreadP();
+}
+
+DWORD PCM_AudioOut::ThreadP()
+{
+ HANDLE hs[2]={g_hSem,g_hEvent};
+ while (1)
+ {
+ int i;
+ i=WaitForMultipleObjects(2,hs,FALSE,INFINITE);
+ if (g_quit_flag) break;
+ if (i == WAIT_OBJECT_0)
+ {
+ int x;
+ for (x = 0; x < MAX_NUM_BLOCKS && !(g_wave_headers[x].dwFlags & WHDR_DONE); x++);
+ if (x < MAX_NUM_BLOCKS)
+ {
+ EnterCriticalSection(&g_cs);
+ if (g_wave_headers[x].dwFlags & WHDR_DONE)
+ {
+ int r=g_wave_headers[x].dwBufferLength;
+ g_outtime_interp=GetTickCount();
+ g_buffer_valid -=r;
+ g_buffer_inlength-=r;
+ g_outtime_bytes +=r;
+ waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
+ g_wave_headers[x].dwFlags=0;
+ g_wavecnt--;
+ i++;
+ }
+ LeaveCriticalSection(&g_cs);
+ }
+ }
+ if (i == WAIT_OBJECT_0+1 && !g_pause)
+ {
+ int l;
+ int m;
+ int t=num_blocks;
+ EnterCriticalSection(&g_cs);
+ l=g_buffer_valid-g_buffer_inlength;
+ LeaveCriticalSection(&g_cs);
+ again:
+ if (g_quit_flag) break;
+ if (g_writeall && l<max_blocksize)
+ {
+ ispcnt=0;
+ g_writeall=0;
+ m=1;
+ }
+ else
+ {
+ m=MAX(MIN(g_buffer_length/2,min_blocksize),g_prebuf);
+ l&=~1023;
+ }
+ if (l >= m && g_wavecnt < t)
+ {
+ int x;
+ if (l > max_blocksize) l=max_blocksize;
+ for (x = 0; x < t && (g_wave_headers[x].dwFlags & WHDR_PREPARED); x++);
+ if (x < t)
+ {
+ int ml=(g_buffer+g_buffer_length)-g_buffer_read;
+ if (l > g_buffer_length) l=g_buffer_length;
+ if (l>ml)
+ {
+ int addlen=l-ml;
+ if (addlen > 65536) addlen=65536;
+ if (addlen > g_buffer_length) addlen=g_buffer_length;
+ memcpy(g_buffer+g_buffer_length,g_buffer,addlen);
+ }
+
+ g_wave_headers[x].dwBufferLength=l;
+ g_wave_headers[x].lpData=g_buffer_read;
+
+ if (waveOutPrepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0])) == MMSYSERR_NOERROR)
+ {
+ do_samples_altvol(g_wave_headers[x].lpData,g_wave_headers[x].dwBufferLength);
+
+ if (waveOutWrite(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0])) == MMSYSERR_NOERROR)
+ {
+ g_prebuf=0;
+
+ g_wavecnt++;
+
+ g_buffer_inlength += l;
+ g_buffer_read += l;
+
+ if (g_buffer_read >= g_buffer+g_buffer_length) g_buffer_read-=g_buffer_length;
+
+ if (g_wavecnt < t)
+ {
+ EnterCriticalSection(&g_cs);
+ l=g_buffer_valid-g_buffer_inlength;
+ LeaveCriticalSection(&g_cs);
+ if (l >= m) goto again;
+ }
+ }
+ else
+ {
+ waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
+ g_wave_headers[x].dwFlags=0;
+ }
+ }
+ else g_wave_headers[x].dwFlags=0;
+ }
+ }
+ }
+ }
+ g_quit_flag=2;
+ return 0;
+}
+
+void PCM_AudioOut::do_samples_altvol(char *in, int blen)
+{
+ if ((a_v != 255 || a_p) && g_vol_table)
+ {
+ EnterCriticalSection(&g_cs);
+ if (g_bps == 8)
+ {
+ unsigned char *i=(unsigned char *)in;
+ int x = blen;
+ if (g_nch==1)
+ {
+ while (x--) { *i = g_vol_table[*i]; i ++; }
+ }
+ else
+ {
+ x>>=1;
+ while (x--)
+ {
+ i[0] = g_vol_table[i[0]];
+ i[1] = g_vol_table[i[1] + 256];
+ i+=2;
+ }
+ }
+ }
+ else if (g_bps == 16)
+ {
+ short *i = (short *) in;
+ short *tab= (short *)g_vol_table;
+ int x = blen>>1;
+ if (g_nch==1)
+ {
+ while (x--)
+ {
+ int a = i[0];
+ if (a <= 0) i[0] = tab[-a];
+ else i[0] = -tab[a];
+ i++;
+ }
+ }
+ else
+ {
+ x>>=1;
+ while (x--)
+ {
+ int a = i[0];
+ if (a <= 0) i[0] = tab[-a];
+ else i[0] = -tab[a];
+ a=i[1];
+ if (a <= 0) i[1] = tab[32769-a];
+ else i[1] = -tab[32769+a];
+ i+=2;
+ }
+ }
+ }
+ LeaveCriticalSection(&g_cs);
+ }
+}
+
+void PCM_AudioOut::setvolume(int volume)
+{
+ if (volume >= 0 && volume <= 255) a_v=volume;
+ _setvol();
+}
+
+void PCM_AudioOut::setpan(int pan)
+{
+ if (pan >= -255 && pan <= 255) a_p=pan;
+ _setvol();
+}
+
+
+IAudioOutput *PCMOUT_CREATE(unsigned int outfmt[8])
+{
+ if (outfmt[0] != NSV_MAKETYPE('P','C','M',' ') ||
+ !outfmt[1] || !outfmt[2] || !outfmt[3]) return NULL;
+ PCM_AudioOut *p=new PCM_AudioOut(outfmt[1],outfmt[2],outfmt[3]);
+ if (p->open_success()) return p;
+ delete p;
+ return NULL;
+} \ No newline at end of file
diff --git a/Src/nsv/nsvplay/audiostub.h b/Src/nsv/nsvplay/audiostub.h
new file mode 100644
index 00000000..df9833fd
--- /dev/null
+++ b/Src/nsv/nsvplay/audiostub.h
@@ -0,0 +1,8 @@
+#ifndef _AUDIOSTUB_H_
+#define _AUDIOSTUB_H_
+
+#include "../dec_if.h"
+
+IAudioOutput *PCMOUT_CREATE(unsigned int outfmt[8]);
+
+#endif \ No newline at end of file
diff --git a/Src/nsv/nsvplay/decoders.cpp b/Src/nsv/nsvplay/decoders.cpp
new file mode 100644
index 00000000..82cba2bd
--- /dev/null
+++ b/Src/nsv/nsvplay/decoders.cpp
@@ -0,0 +1,675 @@
+#include <windows.h>
+#include "api.h"
+#include "main.h"
+#include "vfw.h"
+#include <api/service/services.h>
+#include "../nsv/svc_nsvFactory.h"
+#include <api/service/waservicefactory.h>
+#include "../../Winamp/in2.h"
+extern In_Module mod;
+
+// you should probably override these in your project settings
+
+// builtin decoders
+//#define BUILTIN_MP3_SUPPORT
+//#define BUILTIN_VP3_SUPPORT
+//#define BUILTIN_DIVX_SUPPORT
+//#define BUILTIN_PCM_SUPPORT
+//#define BUILTIN_VFW_SUPPORT
+
+// support dll decoders?
+//#define DLL_DECODER_SUPPORT
+
+//#define DLL_DECODER_SUPPORT_NOCURDIR
+
+#ifdef WINAMP_PLUGIN
+# ifndef DLL_DECODER_SUPPORT
+# define DLL_DECODER_SUPPORT
+# endif
+# ifndef DLL_DECODER_SUPPORT_NOCURDIR
+# define DLL_DECODER_SUPPORT_NOCURDIR
+# endif
+# ifndef DLL_DECODER_SUPPORT_IN_
+# define DLL_DECODER_SUPPORT_IN_
+# endif
+# ifndef BUILTIN_PCM_SUPPORT
+# define BUILTIN_PCM_SUPPORT
+# endif
+#endif
+
+#ifdef BUILTIN_VP3_SUPPORT
+#include "vp3stub.h"
+#endif
+#ifdef BUILTIN_VP5_SUPPORT
+#include "vp5stub.h"
+#endif
+#ifdef BUILTIN_MP3_SUPPORT
+#include "mp3stub.h"
+#endif
+
+#ifdef BUILTIN_VFW_SUPPORT
+
+class Gen_Decoder : public IVideoDecoder {
+ public:
+ Gen_Decoder(int w, int h);
+ ~Gen_Decoder();
+ int decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf);
+ void flush() { }
+
+ int m_err;
+
+ int width,height;
+ BITMAPINFO gen_bmo,gen_bmi;
+ HIC gen_hic;
+ unsigned char *vidbufdec;
+};
+
+Gen_Decoder::Gen_Decoder(int w, int h)
+{
+ width=w;
+ height=h;
+ m_err=0;
+ gen_hic=0;
+ vidbufdec=(unsigned char*)malloc(sizeof(YV12_PLANES) + w*h*3/2);
+}
+
+Gen_Decoder::~Gen_Decoder()
+{
+ if (gen_hic)
+ {
+ ICDecompressEnd(gen_hic);
+ ICClose(gen_hic);
+ }
+ free(vidbufdec);
+}
+
+
+int Gen_Decoder::decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf)
+{
+ *out_type=NSV_MAKETYPE('Y','V','1','2');
+ gen_bmi.bmiHeader.biSizeImage = in_len;
+ if(ICERR_OK == ICDecompress(gen_hic,0,(BITMAPINFOHEADER *) &gen_bmi, (char*)in,(BITMAPINFOHEADER *) &gen_bmo, (char*)vidbufdec+sizeof(YV12_PLANES)))
+ {
+ //*is_kf=!(!in_len || ((unsigned char *)in)[0] > 0x7f);
+ *is_kf=1;
+
+ if (need_kf && !*is_kf)
+ {
+ return 0;
+ }
+ YV12_PLANES *image_vbd=(YV12_PLANES *)vidbufdec;
+ image_vbd->y.baseAddr=(unsigned char *)(image_vbd+1);
+ image_vbd->v.baseAddr=((unsigned char *)(image_vbd+1)) + width*height;
+ image_vbd->u.baseAddr=((unsigned char *)(image_vbd+1)) + width*height*5/4;
+ image_vbd->y.rowBytes=width;
+ image_vbd->v.rowBytes=width/2;
+ image_vbd->u.rowBytes=width/2;
+ *out=(void*)vidbufdec;
+
+ return 0;
+ }
+
+ return -1;
+
+}
+
+
+static IVideoDecoder *createVfw(int w, int h, double framerate, unsigned int type, int *flip)
+{
+ HIC gen_hic = ICOpen(ICTYPE_VIDEO, type, ICMODE_DECOMPRESS);
+
+ if (!gen_hic) return 0;
+
+ BITMAPINFO gen_bmo={0,},gen_bmi={0,};
+ gen_bmi.bmiHeader.biSize=sizeof(gen_bmi.bmiHeader);
+ gen_bmi.bmiHeader.biCompression = type;
+ gen_bmi.bmiHeader.biHeight=h;
+ gen_bmi.bmiHeader.biWidth =w;
+ gen_bmi.bmiHeader.biPlanes=1;
+
+ gen_bmo.bmiHeader.biSize=sizeof(gen_bmo.bmiHeader);
+ gen_bmo.bmiHeader.biCompression = mmioFOURCC('Y','V','1','2');
+ gen_bmo.bmiHeader.biHeight=h;
+ gen_bmo.bmiHeader.biWidth =w;
+ gen_bmo.bmiHeader.biSizeImage=(w*h*3)/2;
+ gen_bmo.bmiHeader.biPlanes=1;
+ gen_bmo.bmiHeader.biBitCount=12;
+
+
+ if (ICERR_OK !=ICDecompressBegin(gen_hic, &gen_bmi, &gen_bmo))
+ {
+ ICClose(gen_hic);
+ return 0;
+ }
+ Gen_Decoder *t=new Gen_Decoder(w,h);
+ t->gen_bmi=gen_bmi;
+ t->gen_bmo=gen_bmo;
+ t->gen_hic=gen_hic;
+
+ return t;
+}
+
+#endif
+
+
+#ifdef BUILTIN_DIVX_SUPPORT
+#include "../../divx5/decore.h"
+
+class CrapDivxDecoder : public IVideoDecoder {
+ public:
+ CrapDivxDecoder(int w, int h)
+ {
+ predict_keyframes=1;
+ divx_param.x_dim = w;
+ divx_param.y_dim = h;
+ divx_param.output_format = DEC_USER;
+ divx_param.codec_version = 412; // indicates that the stream is DivX 4.12 compatible
+ divx_param.build_number = 0; // in this case, the build field is ignored
+ divx_param.time_incr = 15; // time_incr default value
+
+ g_decore((long) this, DEC_OPT_MEMORY_REQS, &divx_param, &decMemReqs);
+
+ // the application allocates the data structures and the buffers
+ divx_param.buffers.mp4_edged_ref_buffers = malloc(decMemReqs.mp4_edged_ref_buffers_size);
+ divx_param.buffers.mp4_edged_for_buffers = malloc(decMemReqs.mp4_edged_for_buffers_size);
+ divx_param.buffers.mp4_edged_back_buffers = malloc(decMemReqs.mp4_edged_back_buffers_size);
+ divx_param.buffers.mp4_display_buffers = malloc(decMemReqs.mp4_display_buffers_size);
+ divx_param.buffers.mp4_state = malloc(decMemReqs.mp4_state_size);
+ divx_param.buffers.mp4_tables = malloc(decMemReqs.mp4_tables_size);
+ divx_param.buffers.mp4_stream = malloc(decMemReqs.mp4_stream_size);
+ divx_param.buffers.mp4_reference = malloc(decMemReqs.mp4_reference_size);
+
+ memset(divx_param.buffers.mp4_state, 0, decMemReqs.mp4_state_size);
+ memset(divx_param.buffers.mp4_tables, 0, decMemReqs.mp4_tables_size);
+ memset(divx_param.buffers.mp4_stream, 0, decMemReqs.mp4_stream_size);
+ memset(divx_param.buffers.mp4_reference, 0, decMemReqs.mp4_reference_size);
+
+ g_decore((long) this, DEC_OPT_INIT, &divx_param, NULL);
+ }
+ ~CrapDivxDecoder()
+ {
+ if (g_decore)
+ {
+ g_decore((long) this,DEC_OPT_RELEASE,NULL,NULL);
+ free(divx_param.buffers.mp4_display_buffers);
+ free(divx_param.buffers.mp4_edged_for_buffers);
+ free(divx_param.buffers.mp4_edged_back_buffers);
+ free(divx_param.buffers.mp4_edged_ref_buffers);
+ free(divx_param.buffers.mp4_reference);
+ free(divx_param.buffers.mp4_state);
+ free(divx_param.buffers.mp4_stream);
+ free(divx_param.buffers.mp4_tables);
+ }
+ if (!--divx_cnt)
+ {
+ FreeModule(hDivxLib);
+ hDivxLib=0;
+ g_decore=0;
+ }
+ }
+ int decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf)
+ {
+ *out_type=NSV_MAKETYPE('Y','V','1','2');
+ *out=NULL;
+ int kfpredict=0;
+ if (predict_keyframes && in_len>3)
+ {
+ kfpredict=!((unsigned char *)in)[3];
+ if (need_kf && !kfpredict) return 0;
+ }
+ if (!in_len) return 0;
+ *is_kf=kfpredict;
+
+ DEC_PICTURE pic;
+ DEC_FRAME decFrame;
+
+ decFrame.bitstream = in;
+ decFrame.bmp = &pic;
+ decFrame.length = in_len;
+ decFrame.render_flag = 1;
+
+ DEC_FRAME_INFO fi;
+
+ if (g_decore((long) this, DEC_OPT_FRAME, &decFrame, &fi) == DEC_OK)
+ {
+ if (!kfpredict != !fi.intra) predict_keyframes=0;
+ *is_kf=fi.intra;
+ if (need_kf && !fi.intra) return 0;
+
+ image_vbd.y.baseAddr=(unsigned char *)pic.y;
+ image_vbd.u.baseAddr=(unsigned char *)pic.u;
+ image_vbd.v.baseAddr=(unsigned char *)pic.v;
+ image_vbd.y.rowBytes=pic.stride_y;
+ image_vbd.u.rowBytes=pic.stride_uv;
+ image_vbd.v.rowBytes=pic.stride_uv;
+
+ *out=&image_vbd;
+ return 0;
+ }
+
+ return -1;
+ }
+
+ void flush() { }
+
+
+ static int (STDCALL *g_decore)(
+ unsigned long handle, // handle - the handle of the calling entity, must be unique
+ unsigned long dec_opt, // dec_opt - the option for docoding, see below
+ void *param1, // param1 - the parameter 1 (it's actually meaning depends on dec_opt
+ void *param2); // param2 - the parameter 2 (it's actually meaning depends on dec_opt
+ static HINSTANCE hDivxLib;
+ static int divx_cnt;
+
+ private:
+ DEC_PARAM divx_param;
+ YV12_PLANES image_vbd;
+ DEC_MEM_REQS decMemReqs;
+ int predict_keyframes;
+};
+
+int (STDCALL *CrapDivxDecoder::g_decore)(
+ unsigned long handle, // handle - the handle of the calling entity, must be unique
+ unsigned long dec_opt, // dec_opt - the option for docoding, see below
+ void *param1, // param1 - the parameter 1 (it's actually meaning depends on dec_opt
+ void *param2)=0; // param2 - the parameter 2 (it's actually meaning depends on dec_opt
+HINSTANCE CrapDivxDecoder::hDivxLib=0;
+int CrapDivxDecoder::divx_cnt=0;
+
+IVideoDecoder *DIVX_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip)
+{
+ if (fmt == NSV_MAKETYPE('D','i','v','X'))
+ {
+ if (!CrapDivxDecoder::divx_cnt)
+ {
+ CrapDivxDecoder::hDivxLib=LoadLibrary("divx.dll");
+ if (CrapDivxDecoder::hDivxLib) *((void**)&CrapDivxDecoder::g_decore)=GetProcAddress(CrapDivxDecoder::hDivxLib,"decore");
+ }
+ CrapDivxDecoder::divx_cnt++;
+ if (CrapDivxDecoder::g_decore) return new CrapDivxDecoder(w,h);
+ }
+ return NULL;
+}
+
+#endif // end of divx gayness
+
+class NullVideoDecoder : public IVideoDecoder
+{
+ public:
+ NullVideoDecoder() { }
+ ~NullVideoDecoder() { }
+ int decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf)
+ {
+ *out_type=NSV_MAKETYPE('Y','V','1','2');
+ *is_kf=1;
+ *out=NULL;
+ return 0;
+ }
+ void flush() { }
+};
+
+
+class NullAudioDecoder : public IAudioDecoder
+{
+ public:
+ NullAudioDecoder(){}
+ ~NullAudioDecoder(){}
+ int decode(void *in, int in_len,
+ void *out, int *out_len,
+ unsigned int out_fmt[8])
+ {
+ *out_len=0;
+ out_fmt[0]=NSV_MAKETYPE('N','O','N','E'); // no output
+ return 0;
+ }
+ void flush(){}
+};
+
+#ifdef BUILTIN_PCM_SUPPORT
+class PCMAudioDecoder : public IAudioDecoder
+{
+ public:
+ PCMAudioDecoder() { fused=4; }
+ ~PCMAudioDecoder(){}
+ int decode(void *in, int in_len,
+ void *out, int *out_len,
+ unsigned int out_fmt[8])
+ {
+ if (in_len < 4)
+ {
+ *out_len=0;
+ out_fmt[0]=0;
+ return 0; // screw this frame
+ }
+ unsigned char *t=(unsigned char *)in;
+ int bps=t[0];
+ int nch=t[1];
+ int srate=((int)t[2] | (((int)t[3])<<8));
+
+ out_fmt[0]=NSV_MAKETYPE('P','C','M',' ');
+ out_fmt[1]=srate;
+ out_fmt[2]=nch;
+ out_fmt[3]=bps;
+
+ int l=in_len-fused;
+ if (l > *out_len) l = *out_len;
+ l&=~(nch*(bps/8)-1);
+
+ if (l) memcpy(out,(char *)in + fused,l);
+ fused+=l;
+ *out_len=l;
+
+ if (fused >= in_len)
+ {
+ fused=4;
+ return 0;
+ }
+ return 1;
+ }
+ void flush() { fused=4; }
+ private:
+ int fused;
+};
+#endif
+
+#ifdef DLL_DECODER_SUPPORT
+static char DLL_Dir[MAX_PATH];
+static HINSTANCE DLL_Handles[512];
+#endif
+
+
+void Decoders_Init(char *wapluginspath)
+{
+#ifdef DLL_DECODER_SUPPORT
+ HKEY hKey;
+
+ if (!DLL_Dir[0] && RegOpenKeyExA(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion",
+ 0,KEY_READ,&hKey) == ERROR_SUCCESS)
+ {
+ DWORD l = sizeof(DLL_Dir);
+ DWORD t;
+ if (RegQueryValueExA(hKey,"CommonFilesDir",NULL,&t,(LPBYTE)DLL_Dir,&l ) != ERROR_SUCCESS || t != REG_SZ) DLL_Dir[0]=0;
+ DLL_Dir[sizeof(DLL_Dir)-5]=0;
+ CreateDirectoryA(DLL_Dir,NULL);
+ strcat(DLL_Dir,"\\NSV");
+ CreateDirectoryA(DLL_Dir,NULL);
+ RegCloseKey(hKey);
+ }
+
+ if (!DLL_Dir[0]) GetTempPathA(sizeof(DLL_Dir),DLL_Dir);
+ Decoders_Quit();
+
+ HANDLE h;
+ int x=0;
+ WIN32_FIND_DATAA fd = {0};
+ char buf[MAX_PATH*2+1] = {0};
+
+#ifndef DLL_DECODER_SUPPORT_NOCURDIR
+ char curdir[MAX_PATH] = {0};
+
+ strcpy( curdir, ".\\" );
+
+ strcpy( buf, curdir );
+ strcat( buf, "nsvdec_*.dll" );
+
+ OutputDebugString( buf ); OutputDebugString( "\n" );
+
+ h = FindFirstFile(buf,&fd);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ strcpy(buf,curdir);
+ strcat(buf,fd.cFileName);
+
+ DLL_Handles[x]=LoadLibrary(buf);
+ if (DLL_Handles[x])
+ {
+ if (GetProcAddress(DLL_Handles[x],"CreateVideoDecoder") ||
+ GetProcAddress(DLL_Handles[x],"CreateAudioDecoder")) x++;
+ else
+ {
+ FreeLibrary(DLL_Handles[x]);
+ DLL_Handles[x]=0;
+ }
+ }
+ } while (x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && FindNextFile(h,&fd));
+ FindClose(h);
+ }
+#endif
+
+#ifdef DLL_DECODER_SUPPORT_IN_
+
+ if (wapluginspath && wapluginspath[0])
+ {
+ lstrcpynA(buf,wapluginspath,sizeof(buf)-16);
+ strcat(buf,"\\in_*.dll");
+ h = FindFirstFileA(buf,&fd);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ strncpy(buf, wapluginspath, MAX_PATH);
+ strncat(buf, "\\", MAX_PATH);
+ strncat(buf, fd.cFileName, MAX_PATH);
+
+ DLL_Handles[x]=LoadLibraryA(buf);
+ if (DLL_Handles[x])
+ {
+ if (GetProcAddress(DLL_Handles[x],"CreateVideoDecoder") ||
+ GetProcAddress(DLL_Handles[x],"CreateAudioDecoder")) x++;
+ else
+ {
+ FreeLibrary(DLL_Handles[x]);
+ DLL_Handles[x]=0;
+ }
+ }
+ } while (x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && FindNextFileA(h,&fd));
+ FindClose(h);
+ }
+ lstrcpynA(buf,wapluginspath,sizeof(buf)-16);
+ strcat(buf,"\\nsvdec_*.dll");
+ h = FindFirstFileA(buf,&fd);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ strncpy(buf, wapluginspath, MAX_PATH);
+ strncat(buf, "\\", MAX_PATH);
+ strncat(buf, fd.cFileName, MAX_PATH);
+
+ DLL_Handles[x]=LoadLibraryA(buf);
+ if (DLL_Handles[x])
+ {
+ if (GetProcAddress(DLL_Handles[x],"CreateVideoDecoder") ||
+ GetProcAddress(DLL_Handles[x],"CreateAudioDecoder")) x++;
+ else
+ {
+ FreeLibrary(DLL_Handles[x]);
+ DLL_Handles[x]=0;
+ }
+ }
+ } while (x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && FindNextFileA(h,&fd));
+ FindClose(h);
+ }
+ }
+#endif
+
+#ifndef WINAMPX
+ strncpy(buf, DLL_Dir, MAX_PATH);
+ strncat(buf, "\\nsvdec_*.dll", MAX_PATH);
+ h = FindFirstFileA(buf,&fd);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ strncpy(buf, DLL_Dir, MAX_PATH);
+ strncat(buf, "\\", MAX_PATH);
+ strncat(buf, fd.cFileName, MAX_PATH);
+
+ DLL_Handles[x]=LoadLibraryA(buf);
+ if (DLL_Handles[x])
+ {
+ if (GetProcAddress(DLL_Handles[x],"CreateVideoDecoder") ||
+ GetProcAddress(DLL_Handles[x],"CreateAudioDecoder")) x++;
+ else
+ {
+ FreeLibrary(DLL_Handles[x]);
+ DLL_Handles[x]=0;
+ }
+ }
+ } while (x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && FindNextFileA(h,&fd));
+ FindClose(h);
+ }
+#endif
+
+#endif
+}
+
+
+void Decoders_Quit()
+{
+#ifdef DLL_DECODER_SUPPORT
+ int x;
+ for (x = 0; x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && DLL_Handles[x]; x ++)
+ {
+ FreeLibrary(DLL_Handles[x]);
+ DLL_Handles[x]=0;
+ }
+#endif
+}
+
+static IAudioDecoder *CreateAudioDecoderWasabi(unsigned int type, IAudioOutput **output)
+{
+ int n = 0;
+ waServiceFactory *sf = 0;
+ while (sf = mod.service->service_enumService(WaSvc::NSVFACTORY, n++))
+ {
+ svc_nsvFactory *factory = (svc_nsvFactory *)sf->getInterface();
+ if (factory)
+ {
+ IAudioDecoder *decoder = factory->CreateAudioDecoder(type, output);
+ sf->releaseInterface(factory);
+ if (decoder)
+ return decoder;
+ }
+ }
+ return 0;
+}
+
+static IVideoDecoder *CreateVideoDecoderWasabi(int w, int h, double framerate, unsigned int type, int *flip)
+{
+ int n=0;
+ waServiceFactory *sf = 0;
+ while (sf = mod.service->service_enumService(WaSvc::NSVFACTORY, n++))
+ {
+ svc_nsvFactory *factory = (svc_nsvFactory *)sf->getInterface();
+ if (factory)
+ {
+ IVideoDecoder *decoder = factory->CreateVideoDecoder(w, h, framerate, type, flip);
+ sf->releaseInterface(factory);
+ if (decoder)
+ return decoder;
+ }
+ }
+ return 0;
+}
+
+IAudioDecoder *CreateAudioDecoder(unsigned int type, int *wasNotNull, IAudioOutput **output)
+{
+ IAudioDecoder *a=NULL;
+ if (mod.service && !a)
+ a = CreateAudioDecoderWasabi(type, output);
+#ifdef BUILTIN_MP3_SUPPORT
+ if (!a) a=MP3_CREATE(type);
+#endif
+#ifdef BUILTIN_PCM_SUPPORT
+ if (!a && type == NSV_MAKETYPE('P','C','M',' ')) a=new PCMAudioDecoder;
+#endif
+#ifdef BUILTIN_AAC_SUPPORT
+ extern IAudioDecoder *AAC_CREATE(unsigned int fmt, IAudioOutput **output);
+ if (!a && (type == NSV_MAKETYPE('A','A','C',' ') || type == NSV_MAKETYPE('V','L','B',' '))) a=AAC_CREATE(type,NULL);
+#endif
+#ifdef BUILTIN_AACP_SUPPOT
+ extern IAudioDecoder *AACP_CREATE(unsigned int fmt, IAudioOutput **output);
+ if (!a && (type == NSV_MAKETYPE('A','A','C','P') || type == NSV_MAKETYPE('A','A','C',' '))) a=AAC_CREATE(type,NULL);
+#endif
+#ifdef DLL_DECODER_SUPPORT
+ int x;
+ for (x = 0; !a && x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && DLL_Handles[x]; x ++)
+ {
+ IAudioDecoder *(*cad)(unsigned int type, IAudioOutput **output);
+ *((void**)&cad) = (void*)GetProcAddress(DLL_Handles[x],"CreateAudioDecoder");
+ if (cad) a=cad(type,output);
+ }
+#endif
+
+ if (!a)
+ {
+ *wasNotNull=0;
+ void *mem = WASABI_API_MEMMGR->sysMalloc(sizeof(NullAudioDecoder));
+ a = new (mem) NullAudioDecoder();
+ }
+ else *wasNotNull=1;
+ return a;
+}
+
+IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int type, int *flip, int *wasNotNull)
+{
+ IVideoDecoder *v=NULL;
+ if (mod.service && !v)
+ v = CreateVideoDecoderWasabi(w, h, framerate, type, flip);
+#ifdef BUILTIN_DIVX_SUPPORT
+ if (!v) v=DIVX_CREATE(w,h,framerate,type,flip);
+#endif
+#ifdef BUILTIN_VP3_SUPPORT
+ if (!v) v=VP3_CREATE(w,h,framerate,type,flip);
+#endif
+#ifdef BUILTIN_VP5_SUPPORT
+ if (!v) v=VP5_CREATE(w,h,framerate,type,flip);
+#endif
+#ifdef BUILTIN_VP6_SUPPORT
+ extern IVideoDecoder *VP6_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip);
+ if (!v) v=VP6_CREATE(w,h,framerate,type,flip);
+#endif
+#ifdef DLL_DECODER_SUPPORT
+ int x;
+ for (x = 0; !v && x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && DLL_Handles[x]; x ++)
+ {
+ IVideoDecoder *(*cvd)(int w, int h, double framerate, unsigned int type, int *flip);
+ *((void**)&cvd) = (void*)GetProcAddress(DLL_Handles[x],"CreateVideoDecoder");
+ if (cvd) v=cvd(w,h,framerate,type,flip);
+ }
+#endif
+#ifdef BUILTIN_VFW_SUPPORT
+ if (!v)
+ {
+ v=createVfw(w,h,framerate,type,flip);
+ }
+#endif
+ if (!v)
+ {
+ if (wasNotNull) *wasNotNull=0;
+ void *mem = WASABI_API_MEMMGR->sysMalloc(sizeof(NullVideoDecoder));
+ v = new (mem) NullVideoDecoder();
+ }
+ else if (wasNotNull) *wasNotNull=1;
+
+ return v;
+} \ No newline at end of file
diff --git a/Src/nsv/nsvplay/main.cpp b/Src/nsv/nsvplay/main.cpp
new file mode 100644
index 00000000..76f2f5c1
--- /dev/null
+++ b/Src/nsv/nsvplay/main.cpp
@@ -0,0 +1,230 @@
+#include <windows.h>
+
+#include "main.h"
+#include "video.h"
+#include "resource.h"
+
+typedef struct
+{
+ VideoOutput *vidOut;
+ NSVDecoder *decode;
+ int quit;
+} parms;
+
+HINSTANCE g_hInstance;
+int g_bitmap_id=IDB_BITMAP1;
+
+//#define NO_ABOUT_EGG
+
+extern long glCounterNSVf, glSyncFrameCount, glNonSyncFrameCount;
+
+#define WNDMENU_CAPTION "NSVPlay/0.994"
+#include "about.h"
+#include "wndmenu.h"
+
+DWORD WINAPI pooThread(LPVOID p)
+{
+ parms *parm=(parms*)p;
+ unsigned int last_title_check=0;
+
+ while (!parm->quit)
+ {
+ int r=parm->decode->run(&parm->quit);
+ if (r < 0)
+ {
+ if (parm->decode->get_error()) MessageBox(parm->vidOut->getHwnd(),parm->decode->get_error(),"NSV Player Error",MB_OK|MB_ICONSTOP);
+ break;
+ }
+ else if (!r)
+ {
+ if ((GetTickCount()-last_title_check) > 1000)
+ {
+ char *v=parm->decode->getTitle();
+ char *buf=(char*)malloc((v?strlen(v):0)+128);
+ int posms=parm->decode->getpos();
+ int lenms=parm->decode->getlen();
+ char *s=parm->decode->getStatus();
+ if (lenms != ~0)
+ wsprintf(buf,"%s [%d:%02d/%d:%02d] @ %dkbps %s%s%s- NSV Player",v?v:"",
+ posms/60000,(posms/1000)%60,
+ lenms/60000,(lenms/1000)%60,
+ parm->decode->getBitrate()/1000,
+ s?"[":"",s?s:"",s?"] ":""
+ );
+ else
+ wsprintf(buf,"%s [%d:%02d] @ %dkbps %s%s%s- NSV Player",v?v:"",
+ posms/60000,(posms/1000)%60,
+ parm->decode->getBitrate()/1000,
+ s?"[":"",s?s:"",s?"] ":""
+ );
+
+ char *p=buf;
+ while (*p)
+ {
+ if (*p == '_') *p=' ';
+ p++;
+ }
+
+ SetWindowText(parm->vidOut->getHwnd(),buf);
+ free(buf);
+
+ last_title_check=GetTickCount();
+ }
+ Sleep(1);
+ }
+ }
+ parm->quit++;
+ return 0;
+}
+
+
+int g_quit;
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmdParam, int nCmdShow)
+{
+ g_hInstance=hInstance;
+
+ BOOL bDisplayInfoAtEnd = FALSE;
+ char *subtitlefile=NULL;
+ char buf[11];
+ lstrcpyn(buf,lpszCmdParam,11);
+
+ if(strstr(lpszCmdParam, "/info")) {
+ bDisplayInfoAtEnd = TRUE;
+ }
+
+ if (!lstrcmpi(buf,"/subtitle="))
+ {
+ lpszCmdParam += 10;
+ char scanc=' ';
+ if (*lpszCmdParam == '\"')
+ {
+ scanc=*lpszCmdParam++;
+ }
+ subtitlefile=lpszCmdParam;
+ while (*lpszCmdParam && *lpszCmdParam != scanc) lpszCmdParam++;
+ *lpszCmdParam++=0;
+ while (*lpszCmdParam == ' ') lpszCmdParam++;
+ }
+
+ if (*lpszCmdParam == '\"')
+ {
+ lpszCmdParam++;
+ char *p=strstr(lpszCmdParam,"\"");
+ if (p) *p=0;
+ }
+
+ if (!*lpszCmdParam)
+ {
+ MessageBox(NULL,"Usage: nsvplay.exe [/subtitle=\"file|url\"] filename.nsv|http://url.nsv","NSV Player",MB_OK|MB_ICONINFORMATION);
+ return 0;
+ }
+
+ int xpos=CW_USEDEFAULT;
+ int ypos=CW_USEDEFAULT;
+
+ _ReadConfigItemInt("xpos",&xpos);
+ _ReadConfigItemInt("ypos",&ypos);
+
+ VideoOutput *vidOut = new VideoOutput(NULL,xpos,ypos);
+ Decoders_Init();
+
+
+ NSVDecoder *decode=new NSVDecoder(lpszCmdParam,vidOut,subtitlefile);
+ vidOut->setNSVDecoder(decode);
+
+ int m_pause=0;
+
+
+ vidOut->vid_vsync=false;
+
+
+ _ReadConfigItemInt("vsync",&vidOut->vid_vsync);
+ _ReadConfigItemInt("overlays",&vidOut->vid_overlays);
+ _ReadConfigItemInt("ddraw",&vidOut->vid_ddraw);
+ _ReadConfigItemInt("aspectadj",&vidOut->vid_aspectadj);
+ _ReadConfigItemInt("use_mixer",&g_audio_use_mixer);
+ {
+ int temp=1;
+ _ReadConfigItemInt("subtitles",&temp);
+ decode->enableSubs(temp);
+ temp=0;
+ _ReadConfigItemInt("subtitles_size",&temp);
+ decode->setSubsFontSize(temp);
+ }
+
+ DWORD id;
+ parms parm={0,};
+ parm.decode=decode;
+ parm.vidOut=vidOut;
+
+ vidOut->setcallback(my_wndcallback,&parm);
+
+ HANDLE hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)pooThread,(LPVOID)&parm,0,&id);
+
+ for (;;)
+ {
+ MSG msg;
+ while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
+ {
+ if (msg.hwnd == vidOut->getHwnd() && msg.message == WM_KEYDOWN)
+ {
+ if (msg.wParam == VK_SPACE) decode->pause(m_pause=!m_pause);
+ if (msg.wParam == VK_LEFT) decode->seek(decode->getpos()-30000);
+ if (msg.wParam == VK_RIGHT) decode->seek(decode->getpos()+30000);
+ if (msg.wParam == VK_UP)
+ {
+ int vol=decode->getvolume()+16;
+ if (vol > 255) vol=255;
+ decode->setvolume(vol);
+ }
+ if (msg.wParam == VK_DOWN)
+ {
+ int vol=decode->getvolume()-16;
+ if (vol < 0) vol=0;
+ decode->setvolume(vol);
+ }
+ if (msg.wParam == VK_RETURN)
+ {
+ if (vidOut->is_fullscreen()) vidOut->remove_fullscreen();
+ else vidOut->fullscreen();
+ }
+ if (msg.wParam == 'v' || msg.wParam == 'V')
+ {
+ vidOut->vid_vsync=!vidOut->vid_vsync;
+ }
+ if (msg.wParam == 'a' || msg.wParam == 'A')
+ {
+ vidOut->vid_aspectadj=!vidOut->vid_aspectadj;
+ PostMessage(vidOut->getHwnd(),WM_TIMER,1,0);
+ }
+ }
+ DispatchMessage(&msg);
+ }
+
+ if (!IsWindow(vidOut->getHwnd()) || parm.quit) break;
+
+ Sleep(50);
+
+ }
+ parm.quit++;
+ WaitForSingleObject(hThread,INFINITE);
+ CloseHandle(hThread);
+ delete decode;
+ delete vidOut;
+
+ Decoders_Quit();
+
+#if _DEBUG
+ // if "/info" on the command line, display some statistics about the movie
+ if(bDisplayInfoAtEnd) {
+ char szMessage[MAX_PATH];
+
+ wsprintf(szMessage, "File Header Count=%ld\nSync Frames=%ld\nNon-sync Frames=%ld", glCounterNSVf, glSyncFrameCount, glNonSyncFrameCount);
+ MessageBox(NULL, szMessage, WNDMENU_CAPTION, MB_OK);
+ }
+#endif
+
+ return 0;
+}
+
diff --git a/Src/nsv/nsvplay/main.h b/Src/nsv/nsvplay/main.h
new file mode 100644
index 00000000..a941cb01
--- /dev/null
+++ b/Src/nsv/nsvplay/main.h
@@ -0,0 +1,248 @@
+#ifndef NSVPLAY_MAIN_H
+#define NSVPLAY_MAIN_H
+
+#include <bfc/platform/types.h>
+#include "../nsvlib.h"
+#include "../dec_if.h"
+
+#define SHOW_STREAM_TITLE_AT_TOP 1
+
+class Subtitles;
+class SubsItem;
+
+#include "IDataReader.h"
+
+
+typedef struct
+{
+ const char *language;
+ const char *utf8_text;
+ unsigned int start_frame, end_frame;
+ unsigned char xPos, yPos;
+ unsigned char colorRed, colorGreen, colorBlue;
+ signed char fontSize;
+ int extraDataSize;
+ const void *extraData;
+} SUBTITLE_INFO;
+
+class IVideoOutput
+{
+ public:
+ virtual ~IVideoOutput() { }
+ virtual int open(int w, int h, int vflip, double aspectratio, unsigned int fmt)=0;
+#ifdef _WIN32
+ virtual void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { }
+#else
+ virtual void setcallback(void *func, void *token) { } // currently unused, here to reserve the spot in the vtable
+#endif
+ virtual void close()=0;
+ virtual void draw(void *frame)=0;
+ virtual void drawSubtitle(SubsItem *item) { }
+ virtual void showStatusMsg(const char *text) { }
+ virtual int get_latency() { return 0; }
+ virtual void notifyBufferState(int bufferstate) { } /* 0-255*/
+
+ virtual intptr_t extended(intptr_t param1, intptr_t param2, intptr_t param3) { return 0; } // Dispatchable, eat this!
+};
+
+template<class T>
+class ClassList {
+public:
+ ~ClassList() {
+ int l=getlen()-1;
+ for(int i=l;i>-1;i--) delete(get(i));
+ }
+ void put(T *item) {
+ m_buf.add(&item,sizeof(T *));
+ }
+ T *get(int n) {
+ if(n>=getlen()) return NULL;
+ return ((T **)m_buf.get())[n];
+ }
+ int getlen() {
+ return (int)(m_buf.getlen()/sizeof(SubsItem *));
+ }
+private:
+ GrowBuf m_buf;
+};
+
+class SubtitlesItem {
+public:
+ SubtitlesItem(const char *language, Subtitles *subs) :
+ m_subs(subs) {
+ m_language=_strdup(language);
+ m_subs=subs;
+ }
+ ~SubtitlesItem() {
+ free((void *)m_language);
+ }
+ const char *m_language;
+ Subtitles *m_subs;
+};
+
+class NSVDecoder {
+
+public:
+
+ NSVDecoder(const char *url, IVideoOutput *output, char *subtitleurl=NULL);
+ ~NSVDecoder();
+
+ char *get_error();
+ char *get_status();
+ int run(int * volatile quit=NULL);
+
+ void pause(int pause);
+ ULONGLONG getpos();
+ unsigned int getpos_frames() { return framecnt; }
+ unsigned int getlen(); // returns 0xFFFFFFFF on unknown
+
+ void setvolume(int volume) { m_volume=volume; if (aud_output) aud_output->setvolume(volume); }
+ void setpan(int pan) { m_pan=pan; if (aud_output) aud_output->setpan(pan); }
+
+ int getvolume() { return m_volume; }
+ int getpan() { return m_pan; }
+
+ int canseek();
+ void seek(unsigned int newpos);
+
+ char *getFromMeta(char *name);
+ char *getUrl() { return m_url; }
+ const char *getServerHeader(char *name);
+
+ char *getTitle();
+ char *getStatus();
+
+ void getAudioDesc(char *buf);
+ void getVideoDesc(char *buf);
+
+ char *getAudioType() { return m_audio_type; }
+ char *getVideoType() { return m_video_type; }
+ unsigned int getFileSize(); // 0xFFFFFFFF if unknown
+ int getBitrate();
+ int getAudioBitrate();
+ int getVideoBitrate();
+ int getWidth() { return unpacket.getWidth(); }
+ int getHeight() { return unpacket.getHeight(); }
+ double getFrameRate() { return use_framerate_override?framerate_override:unpacket.getFrameRate(); }
+ int getBufferPos() { if (m_prebuffer) return m_bufstate; return 256; } // 0-256
+
+ int subsEnabled() { return m_enable_subtitles; }
+ void enableSubs(int e) {
+ m_enable_subtitles=e;
+ if(!e&&m_out) m_out->drawSubtitle(NULL);
+ }
+ int getSubsFontSize() { return m_subs_fontsize; }
+ void setSubsFontSize(int s) {
+ m_subs_fontsize=s;
+ if(m_out) m_out->drawSubtitle(NULL); //will redraw current subtitle with new size
+ }
+
+ void SetPreciseSeeking(int prec) { m_precise_seeking=prec; }
+ void SetBuffering(int total_ms, int initial_ms, int after_underrun);
+ void SetBufferMemoryLimit(int bytes) { m_buf_memlimit=bytes; }
+
+ const char *getSubLanguage(int index);
+ void setSubLanguage(int index) { m_cur_subtitle=index; }
+ int getCurSubLanguage() { return m_cur_subtitle; }
+
+ void CloseVideo()
+ {
+ if (m_out_opened)
+ m_out->close();
+ m_out_opened=0;
+ }
+
+private:
+ ULONGLONG m_avresync_time;
+ int m_pb_init,m_pb_init_ur,m_buffer_total;
+ int m_prebuffer;
+ int m_bufstate;
+
+ int proTimerStart;
+ int proTimerEnd;
+ float profiletime;
+ float prostart;
+ float proend;
+ float timeref;
+
+ int m_again;
+
+ void ProcessSubtitleBlock(void *data, int len);
+
+ int m_paused;
+
+ ULONGLONG hack_l_curpos;
+ ULONGLONG hack_l_curpos_ot;
+
+ int m_buf_memlimit;
+ IVideoOutput *m_out;
+ int m_out_opened;
+
+ nsv_InBS inbs;
+ nsv_InBS audiobs,videobs;
+ nsv_InBS auxbs;
+ int video_frames_avail,audio_frames_avail;
+ nsv_Unpacketer unpacket;
+ unsigned int framecnt;
+ int hdrsearched;
+ int nsvbitstream_search;
+ int64_t m_audio_writepos;
+ unsigned int m_need_seek;
+
+ int seek_dumpframes, seek_dumpaudiosamples;
+ int pcm_samplerate;
+
+ int m_precise_seeking;
+
+ char *m_err;
+ char *m_url;
+ char *m_title;
+
+ int vid_decoder_isnotnull,aud_decoder_isnotnull;
+ IVideoDecoder *vid_decoder;
+ IAudioDecoder *aud_decoder;
+ IAudioOutput *aud_output;
+ IDataReader *file_reader;
+ int needkf;
+ double aspect;
+ double framerate_override;
+ int use_framerate_override;
+
+ nsv_fileHeader fileheader;
+
+ unsigned int avg_framesize_cnt,avg_framesize_tot;
+ unsigned int avg_framesize_cnt_v,avg_framesize_tot_v;
+ unsigned int avg_framesize_cnt_a,avg_framesize_tot_a;
+
+ int vidout_ready;
+ int vid_flip;
+ void *vidout;
+ unsigned int vidout_type;
+ unsigned int vidout_time;
+ unsigned int vidout_codec_width;
+ unsigned int vidout_codec_height;
+
+ char m_audio_type[5];
+ char m_video_type[5];
+
+ int m_volume, m_pan;
+
+ int m_enable_subtitles;
+ int m_subs_fontsize;
+ ClassList<SubtitlesItem> m_subtitles;
+ int m_cur_subtitle;
+
+ Subtitles *insertSubtitlesItem(const char *language, const char *subfile);
+ Subtitles *findSubtitles(const char *language);
+
+};
+
+void Decoders_Init(char *wapluginspath=NULL);
+void Decoders_Quit();
+
+
+IAudioDecoder *CreateAudioDecoder(unsigned int type, int *wasNotNull, IAudioOutput **output);
+IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int type, int *flip, int *wasNotNull=NULL);
+IDataReader *CreateReader(const char *url);
+
+#endif //NSVPLAY_MAIN_H \ No newline at end of file
diff --git a/Src/nsv/nsvplay/mp3stub.cpp b/Src/nsv/nsvplay/mp3stub.cpp
new file mode 100644
index 00000000..72df533c
--- /dev/null
+++ b/Src/nsv/nsvplay/mp3stub.cpp
@@ -0,0 +1,73 @@
+#include "mp3stub.h"
+#include "../../mp3dec/mpgadecoder.h"
+
+int mp3_quality;
+int mp3_downmix;
+
+class MP3_Decoder : public IAudioDecoder
+{
+ public:
+ MP3_Decoder() : mp3_dec(mp3_quality,0,mp3_downmix) { fused=0; pcm_buf_used=0; }
+ ~MP3_Decoder() { };
+ int decode(void *in, int in_len,
+ void *out, int *out_len,
+ unsigned int out_fmt[8]);
+ void flush() { fused=0; pcm_buf_used=0; mp3_dec.Reset(); }
+ private:
+ CMpgaDecoder mp3_dec;
+ char pcm_buf[1152*4*2];
+ int pcm_buf_used;
+ int pcm_offs;
+ int fused;
+};
+
+int MP3_Decoder::decode(void *in, int in_len,
+ void *out, int *out_len,
+ unsigned int out_fmt[8])
+{
+ int rval=1;
+ if (fused < in_len)
+ {
+ int l=mp3_dec.GetInputFree();
+ if (l > in_len-fused) l=in_len-fused;
+ if (l) mp3_dec.Fill((unsigned char *)in + fused,l);
+ fused+=l;
+ }
+
+ if (!pcm_buf_used)
+ {
+ SSC s=mp3_dec.DecodeFrame((unsigned char *)pcm_buf,sizeof(pcm_buf),&pcm_buf_used);
+ pcm_offs=0;
+ }
+
+ if (pcm_buf_used)
+ {
+ int l=*out_len;
+ if (l > pcm_buf_used) l=pcm_buf_used;
+ memcpy(out,pcm_buf+pcm_offs,l);
+ pcm_buf_used-=l;
+ pcm_offs+=l;
+ *out_len=l;
+ }
+ else
+ {
+ if (fused >= in_len) rval=fused=0;
+ *out_len=0;
+ }
+
+ int nch=mp3_dec.m_Info.GetEffectiveChannels();
+ int srate=mp3_dec.m_Info.GetEffectiveSFreq();
+ out_fmt[0]=(nch && srate)?NSV_MAKETYPE('P','C','M',' '):0;
+ out_fmt[1]=srate;
+ out_fmt[2]=nch;
+ out_fmt[3]=(nch && srate)?16:0;
+
+ return rval;
+}
+
+
+IAudioDecoder *MP3_CREATE(unsigned int fmt)
+{
+ if (fmt == NSV_MAKETYPE('M','P','3',' ')) return new MP3_Decoder;
+ return NULL;
+} \ No newline at end of file
diff --git a/Src/nsv/nsvplay/mp3stub.h b/Src/nsv/nsvplay/mp3stub.h
new file mode 100644
index 00000000..11d6a148
--- /dev/null
+++ b/Src/nsv/nsvplay/mp3stub.h
@@ -0,0 +1,12 @@
+#ifndef _MP3STUB_H_
+#define _MP3STUB_H_
+
+#include "main.h"
+
+extern int mp3_quality;
+extern int mp3_downmix;
+extern int mp3_downshit;
+
+IAudioDecoder *MP3_CREATE(unsigned int fmt);
+
+#endif \ No newline at end of file
diff --git a/Src/nsv/nsvplay/nsv_logo.bmp b/Src/nsv/nsvplay/nsv_logo.bmp
new file mode 100644
index 00000000..bcdeaf9d
--- /dev/null
+++ b/Src/nsv/nsvplay/nsv_logo.bmp
Binary files differ
diff --git a/Src/nsv/nsvplay/nsvdecode.cpp b/Src/nsv/nsvplay/nsvdecode.cpp
new file mode 100644
index 00000000..8d8bbcb7
--- /dev/null
+++ b/Src/nsv/nsvplay/nsvdecode.cpp
@@ -0,0 +1,999 @@
+#include <bfc/platform/platform.h>
+#include <bfc/platform/strcmp.h>
+#include <stdio.h>
+#include <locale.h>
+#include "audiostub.h"
+#include "api.h"
+#include "main.h"
+#include "subtitles.h"
+#include "../in_nsv/resource.h"
+
+extern int config_subtitles;
+#ifdef __APPLE__
+#include <sys/time.h>
+#include <unistd.h>
+uint32_t GetTickCount()
+{
+ struct timeval newtime;
+
+ gettimeofday(&newtime, 0);
+ return newtime.tv_sec*1000 + newtime.tv_usec/1000;
+}
+
+int MulDiv(int a, int b, int d)
+{
+ return int ((int64_t)a * (int64_t)b) / (int64_t)d;
+}
+#endif
+class NullAudioOutput : public IAudioOutput
+{
+ public:
+ NullAudioOutput() { m_pause=0; m_pause_t=0; m_start_t=GetTickCount64(); }
+ ~NullAudioOutput(){}
+ int canwrite() { return m_pause ? 0 : 65536; } // returns bytes writeable
+ void write(void *buf, int len) { }
+ ULONGLONG getpos() { return m_pause?m_pause_t:(GetTickCount64()-m_start_t); }
+ int isplaying(void) { return -1; }
+ ULONGLONG getwritepos() { return getpos(); }
+ void flush(unsigned int newtime)
+ {
+ if (m_pause) m_pause_t=newtime;
+ else m_start_t = GetTickCount64()-newtime;
+ }
+ void pause(int pause)
+ {
+ if (pause && !m_pause)
+ {
+ m_pause_t=GetTickCount64()-m_start_t;
+ m_pause=1;
+ }
+ else if (!pause && m_pause)
+ {
+ m_start_t = GetTickCount64()-m_pause_t;
+ m_pause=0;
+ m_pause_t=0;
+ }
+ }
+
+ private:
+ int m_pause;
+ ULONGLONG m_pause_t;
+ ULONGLONG m_start_t;
+};
+
+void NSVDecoder::pause(int pause)
+{
+ if (aud_output) aud_output->pause(pause);
+ m_paused=pause;
+}
+
+ULONGLONG NSVDecoder::getpos()
+{
+ return aud_output ? aud_output->getpos() : 0;
+}
+
+unsigned int NSVDecoder::getlen()
+{
+ if (!fileheader.header_size || fileheader.file_lenms == 0xFFFFFFFF)
+ {
+ if (!file_reader || !avg_framesize_cnt || !avg_framesize_tot) return 0xFFFFFFFF;
+ int s=(int)file_reader->getsize();
+ if (s == 0xFFFFFFFF) return 0xFFFFFFFF;
+
+ int bytespersec=(int)((double)avg_framesize_tot/(double)avg_framesize_cnt*getFrameRate());
+
+ if (!bytespersec) return 0xFFFFFFFF;
+ return MulDiv(s,1000,bytespersec);
+ }
+ return fileheader.file_lenms;
+}
+
+void NSVDecoder::seek(unsigned int newpos)
+{
+ m_need_seek=newpos;
+ m_again=0;
+}
+
+int NSVDecoder::canseek()
+{
+ return file_reader ? file_reader->canseek() : 0;
+}
+
+char *NSVDecoder::getStatus()
+{
+ if (m_prebuffer) return "Buffering";
+ return NULL;
+}
+
+void NSVDecoder::SetBuffering(int total_ms, int initial_ms, int after_underrun)
+{
+ if (m_prebuffer) m_prebuffer=initial_ms;
+ m_pb_init=initial_ms;
+ m_pb_init_ur=after_underrun;
+ m_buffer_total=total_ms;
+}
+
+
+int64_t pdReadResolution(void)
+{
+ int64_t myfeq;
+#ifdef _WIN32
+ LARGE_INTEGER feq;
+
+ QueryPerformanceFrequency( &feq);
+ myfeq = feq.QuadPart;
+#else
+ myfeq = 1000000;
+#endif
+
+ return myfeq;
+}
+
+int64_t pdReadTimer(void)
+{
+ int64_t mynow;
+
+#ifdef _WIN32
+ LARGE_INTEGER now;
+
+ QueryPerformanceCounter( &now );
+ mynow = now.QuadPart;
+#else
+ struct timeval newtime;
+
+ gettimeofday(&newtime,NULL);
+ mynow = (newtime.tv_sec * 1000000) + newtime.tv_usec ;
+#endif
+
+ return mynow;
+}
+
+NSVDecoder::NSVDecoder(const char *url, IVideoOutput *output, char *subtitleurl)
+{
+ m_precise_seeking=1;
+ pcm_samplerate=0;
+ seek_dumpframes=0;
+ seek_dumpaudiosamples=0;
+ m_avresync_time=0;
+ nsvbitstream_search=0;
+ m_paused=0;
+ m_bufstate=0;
+ hack_l_curpos=-1;
+ hack_l_curpos_ot=0;
+ vid_decoder_isnotnull=1;
+ aud_decoder_isnotnull=1;
+ video_frames_avail=0;
+ audio_frames_avail=0;
+ m_buffer_total=1000; // bufferahead
+ m_pb_init=m_prebuffer=1500; // initial prebuffer
+ m_pb_init_ur=1500; // after underrun
+ m_buf_memlimit=32*1024*1024;
+ m_pan=0;
+ m_volume=192;
+ m_title=0;
+ m_audio_type[0]=m_video_type[0]=0;
+ m_out_opened=0;
+ m_out=output;
+ vidout_ready=0;
+ vidout=0;
+ vidout_type=0;
+ vidout_time=0;
+ vidout_codec_width=0;
+ vidout_codec_height=0;
+ m_url=_strdup(url);
+ m_need_seek=~0;
+ needkf=1;
+ aspect=1.0;
+ framerate_override=0.0;
+ use_framerate_override=0;
+ vid_decoder=NULL;
+ aud_decoder=NULL;
+ aud_output=NULL;
+ framecnt=0;
+ hdrsearched=0;
+ file_reader=CreateReader(url);
+ if (!file_reader) m_err="Error opening input";
+ else m_err=NULL;
+
+ m_enable_subtitles=1;
+ m_subs_fontsize=0;
+ m_cur_subtitle=0;
+ if (subtitleurl && *subtitleurl) insertSubtitlesItem("From subtitle file",subtitleurl);
+
+ memset(&fileheader,0,sizeof(fileheader));
+
+ avg_framesize_tot=0;
+ avg_framesize_cnt=0;
+ avg_framesize_tot_v=0;
+ avg_framesize_cnt_v=0;
+ avg_framesize_tot_a=0;
+ avg_framesize_cnt_a=0;
+
+ unpacket.setAudioOut(&audiobs);
+ unpacket.setVideoOut(&videobs);
+ unpacket.setAuxOut(&auxbs);
+
+ proTimerStart = 1;
+ proTimerEnd = 0;
+ profiletime = 0.00;
+ prostart = 0.00;
+ proend = 0.00;
+ timeref = (float)pdReadResolution();
+
+
+ m_again = 0;
+}
+
+NSVDecoder::~NSVDecoder()
+{
+ if (m_out_opened) m_out->close();
+ if (WASABI_API_MEMMGR)
+ WASABI_API_MEMMGR->Delete(vid_decoder);
+ else
+ delete vid_decoder;
+ if (WASABI_API_MEMMGR)
+ WASABI_API_MEMMGR->Delete(aud_decoder);
+ else
+ delete aud_decoder;
+ delete aud_output;
+ delete file_reader;
+ free(fileheader.metadata);
+ free(fileheader.toc);
+ free(m_url);
+ free(m_title);
+}
+
+char *NSVDecoder::get_error()
+{
+ return m_err;
+}
+
+char *NSVDecoder::get_status()
+{
+ return NULL;
+}
+
+char *NSVDecoder::getFromMeta(char *name)
+{
+ if (file_reader)
+ {
+ char *t=(char*)malloc(strlen(name)+8);
+ if (t)
+ {
+ strcpy(t,"x-nsv-");
+ strcat(t,name);
+ char *v=file_reader->getheader(t);
+ free(t);
+ if (v) return _strdup(v);
+ }
+ }
+ return nsv_getmetadata(fileheader.metadata,name);
+}
+
+
+unsigned int NSVDecoder::getFileSize()
+{
+ if (file_reader) return (unsigned int)file_reader->getsize();
+ return ~0;
+}
+
+int NSVDecoder::getBitrate()
+{
+ if (!fileheader.header_size || !fileheader.file_lenms || !fileheader.file_lenbytes ||
+ fileheader.file_lenms == 0xFFFFFFFF || fileheader.file_lenbytes == 0xFFFFFFFF)
+ {
+ if (!avg_framesize_cnt) return 0;
+ return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot / (double)avg_framesize_cnt);
+ }
+ return MulDiv(fileheader.file_lenbytes,8000,fileheader.file_lenms);
+}
+
+const char *NSVDecoder::getServerHeader(char *name)
+{
+ return file_reader?file_reader->getheader(name):NULL;
+}
+
+void NSVDecoder::getAudioDesc(char *buf)
+{
+ char *t=getAudioType();
+ if (t && *t)
+ {
+ if (strcmp(t, "VLB")==0)
+ sprintf(buf,"Dolby AAC "); // special case; make sure the user never sees "VLB" name
+ else if (strcmp(t, "AACP")==0)
+ sprintf(buf,"HE-AAC ");
+ else if (strcmp(t, "AAC")==0)
+ sprintf(buf,"AAC LC ");
+ else
+ sprintf(buf,"%s ",t);
+
+ char *p=buf+strlen(buf);
+ if (aud_output) aud_output->getdescstr(p);
+ if (!*p) *--p=0;
+
+ int a=(getAudioBitrate()+500)/1000;
+ if (a) sprintf(buf+strlen(buf)," ~%d%s",a,WASABI_API_LNGSTRING(IDS_KBPS));
+ }
+ else *buf=0;
+}
+
+void NSVDecoder::getVideoDesc(char *buf)
+{
+ char *t=getVideoType();
+ if (t && *t)
+ {
+ int fr=(int)(getFrameRate()*100.0);
+ sprintf(buf,"%s %dx%d@%d.%02d%s",t,getWidth(),getHeight(),fr/100,fr%100,WASABI_API_LNGSTRING(IDS_FPS));
+
+ int a=(getVideoBitrate()+500)/1000;
+ if (a) sprintf(buf+strlen(buf)," ~%d%s",a,WASABI_API_LNGSTRING(IDS_KBPS));
+ }
+ else *buf=0;
+}
+
+
+int NSVDecoder::getAudioBitrate()
+{
+ if (!avg_framesize_cnt_a) return 0;
+ return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot_a / (double)avg_framesize_cnt_a);
+}
+
+int NSVDecoder::getVideoBitrate()
+{
+ if (!avg_framesize_cnt_v) return 0;
+ return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot_v / (double)avg_framesize_cnt_v);
+}
+
+char *NSVDecoder::getTitle()
+{
+ char *v=getFromMeta("TITLE");
+ if (v)
+ {
+ if (!m_title || strcmp(v,m_title))
+ {
+ free(m_title);
+ m_title=v;
+ }
+ else free(v);
+ return m_title;
+ }
+ if (file_reader)
+ {
+ v=file_reader->gettitle();
+ if (v) return v;
+ }
+
+ if (!m_url) return "";
+
+ v=m_url+strlen(m_url);
+ while (v >= m_url && *v != '/' && *v != '\\' && *v != '=') v--;
+ return ++v;
+}
+
+void NSVDecoder::ProcessSubtitleBlock(void *data, int len)
+{
+ unsigned char *dataptr=(unsigned char *)data;
+ while (len > 2)
+ {
+ int len_block=(dataptr[0] | ((unsigned short)dataptr[1] << 8));
+ dataptr += 2;
+ len -= 2;
+ if (len_block > len) break;
+
+ SUBTITLE_INFO sti={0,};
+ sti.language = (char *)dataptr;
+ while (len_block > 0 && *dataptr)
+ {
+ dataptr++;
+ len_block--;
+ len--;
+ }
+ dataptr++;
+ len_block--;
+ len--;
+ if (len_block < 1) break;
+ sti.utf8_text = (char *)dataptr;
+ while (len_block > 0 && *dataptr)
+ {
+ dataptr++;
+ len_block--;
+ len--;
+ }
+ dataptr++;
+ len_block--;
+ len--;
+ if (len_block < 6) break;
+
+ sti.start_frame = framecnt + video_frames_avail + (dataptr[0] | ((int)dataptr[1] << 8));
+ dataptr+=2;
+ len-=2;
+ len_block-=2;
+
+ sti.end_frame = sti.start_frame + (dataptr[0] | ((int)dataptr[1] << 8) | ((int)dataptr[2] << 16) | ((int)dataptr[3] << 24));
+ dataptr+=4;
+ len-=4;
+ len_block-=4;
+
+ // set defaults for color/position
+ sti.xPos=128;
+ sti.yPos=255;
+ sti.colorRed=255;
+ sti.colorGreen=255;
+ sti.colorBlue=255;
+ sti.fontSize=0;
+
+ if (len_block >= 2)
+ {
+ sti.xPos = *dataptr++;
+ sti.yPos = *dataptr++;
+ len-=2;
+ len_block-=2;
+ if (len_block >= 3)
+ {
+ sti.colorRed=*dataptr++;
+ sti.colorGreen=*dataptr++;
+ sti.colorBlue=*dataptr++;
+ len-=3;
+ len_block-=3;
+ if (len_block > 0)
+ {
+ sti.fontSize=(signed char) *dataptr++;
+ len--;
+ len_block--;
+ }
+
+ if (len_block > 0)
+ {
+ sti.extraData=dataptr;
+ sti.extraDataSize=len_block;
+ }
+
+ }
+ }
+
+ Subtitles *sub=findSubtitles(sti.language);
+ if(!sub) sub=insertSubtitlesItem(sti.language,NULL);
+ sub->addSubtitlePacket(&sti);
+
+ if (len_block > 0)
+ {
+ len-=len_block;
+ dataptr+=len_block;
+ }
+
+ }
+}
+
+int NSVDecoder::run(int * volatile quit) // returns -1 on error, 0 if no frames processed, 1 if frames processed, 2 if underrun
+{
+ int retval=0;
+
+
+ if (!file_reader) return -1;
+
+ if (!aud_decoder_isnotnull && !vid_decoder_isnotnull && vid_decoder && aud_decoder)
+ {
+ m_err="Codec(s) not found";
+ return -1;
+ }
+
+ if (aud_output && vidout_ready)
+ {
+ ULONGLONG curpos=aud_output->getpos();
+ if (!m_paused)
+ {
+ if (!audio_frames_avail && curpos == hack_l_curpos && unpacket.getEof())
+ {
+ hack_l_curpos=curpos;
+ curpos += GetTickCount64() - hack_l_curpos_ot;
+ }
+ else
+ {
+ hack_l_curpos_ot=GetTickCount64();
+ hack_l_curpos=curpos;
+ }
+ }
+
+ if (curpos >= vidout_time && !m_prebuffer)
+ {
+ if (vidout)
+ {
+ // send this to get the flip state updated so when toggled on-the-fly it will work with nsv
+ m_out->extended(0x1002/*VIDUSER_SET_VFLIP*/,vid_flip,0);
+ m_out->draw(vidout);
+ }
+
+ if (m_enable_subtitles)
+ {
+ SubtitlesItem *it=m_subtitles.get(m_cur_subtitle);
+ if(it && config_subtitles) {
+ it->m_subs->setFontSizeModifier(m_subs_fontsize);
+ m_out->drawSubtitle(it->m_subs->getSubtitle((unsigned int)aud_output->getpos(),framecnt));
+ }
+ if ( it && !config_subtitles )
+ {
+ m_out->drawSubtitle(NULL);
+ }
+ }
+ vidout=0;
+ vidout_ready=0;
+ }
+ }
+
+ if (hdrsearched && m_need_seek!=~0 && aud_output)
+ {
+ unsigned int newpos=m_need_seek;
+ m_need_seek=~0;
+ seek_dumpaudiosamples=0;
+ seek_dumpframes=0;
+ if (file_reader->canseek() && file_reader->getsize() != 0xFFFFFFFF)
+ {
+ int nbpos;
+
+ if (!fileheader.toc_size || !fileheader.toc || !fileheader.file_lenms ||
+ !fileheader.file_lenbytes ||
+ fileheader.file_lenms == 0xFFFFFFFF || fileheader.file_lenbytes == 0xFFFFFFFF)
+ {
+ int avg_framesize=avg_framesize_tot/avg_framesize_cnt;
+ int pos_frames=(int)(newpos*getFrameRate());
+ nbpos=fileheader.header_size+MulDiv(pos_frames,avg_framesize,1000);
+ }
+ else // calculate offset using TOC
+ {
+ if (fileheader.toc_ex) // use extended toc, find next earliest time closest
+ {
+ int x;
+ double scale;
+ if (unpacket.isValid() && getFrameRate() > 0.0001)
+ scale=1000.0 / getFrameRate();
+ else scale = 1000.0 / 30.0;
+ for (x = 0; x < (int)fileheader.toc_size; x ++)
+ {
+ if (newpos < (unsigned int) (fileheader.toc_ex[x]*scale)) break;
+ }
+ unsigned int hdr[2]={0,0};
+ if (--x >= 0)
+ {
+ hdr[0]=fileheader.toc[x];
+ hdr[1]=(unsigned int) (fileheader.toc_ex[x] * scale);
+ }
+ //hdr[1] is the time of that keyframe
+
+ if (m_precise_seeking) // precise seek
+ {
+ int timediff = newpos - hdr[1];
+ double fr;
+ if (unpacket.isValid() && getFrameRate() >= 0.0001) fr = getFrameRate() / 1000.0;
+ else fr = 30.0 / 1000.0;
+
+ seek_dumpframes=(int) (timediff * fr);
+ seek_dumpaudiosamples=MulDiv(timediff,pcm_samplerate,1000);
+ }
+ else
+ {
+ newpos=hdr[1];
+ }
+
+ nbpos = fileheader.header_size + hdr[0];
+ }
+ else
+ {
+ double tocpos=(newpos*(double)fileheader.toc_size)/(double)fileheader.file_lenms;
+ unsigned int tocidx=(unsigned int)tocpos;
+ if (tocidx > fileheader.toc_size)
+ {
+ nbpos=fileheader.header_size + fileheader.file_lenbytes;
+ }
+ else
+ {
+ unsigned int a,b;
+ if (tocidx<0) tocidx=0;
+ a = fileheader.toc[tocidx];
+ if (tocidx < fileheader.toc_size-1)
+ b = fileheader.toc[tocidx+1];
+ else b=fileheader.file_lenbytes;
+ double frac=tocpos-tocidx;
+
+ nbpos = fileheader.header_size + (int) (a * (1.0 - frac) + b * frac);
+ }
+ }
+ }
+ if (!file_reader->seek(nbpos))
+ {
+ framecnt=(int)(newpos*getFrameRate()/1000.0);
+ unpacket.reset(0);
+ unpacket.setEof(0);
+ hdrsearched=1;
+ needkf=1;
+ m_avresync_time=0;
+ vidout=0;
+ vidout_ready=0;
+ video_frames_avail=0;
+ audio_frames_avail=0;
+ m_prebuffer=m_pb_init;
+ inbs.clear();
+ audiobs.clear();
+ videobs.clear();
+ auxbs.clear();
+ if (aud_output) aud_output->flush(newpos);
+ if (aud_decoder) aud_decoder->flush();
+ if (vid_decoder) vid_decoder->flush();
+ }
+ }
+ } // end of seeking
+
+ if (!hdrsearched) // search for header
+ {
+readagain_header:
+ if (quit && *quit) return 0;
+ int ret=nsv_readheader(inbs,&fileheader);
+ if (ret <= 0)
+ {
+ hdrsearched++;
+ if (!ret)
+ {
+ _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
+ char *v=getFromMeta("ASPECT");
+ if (v)
+ {
+ aspect=_atof_l(v,C_locale);
+ }
+ free(v);
+ v=getFromMeta("FRAMERATE");
+ if (v)
+ {
+ framerate_override=_atof_l(v,C_locale);
+ if (framerate_override >= 0.01) use_framerate_override=1;
+ free(v);
+ }
+ }
+ }
+ else
+ {
+ char buf[8192] = {0};
+ size_t ret2=file_reader->read(buf,sizeof(buf));
+
+ if (file_reader->iseof()) unpacket.setEof(1);
+
+ if (file_reader->geterror())
+ {
+ m_err=file_reader->geterror();
+ return -1;
+ }
+ if (ret2>0)
+ {
+ inbs.add(buf, (int)ret2);
+ goto readagain_header;
+ }
+ }
+ }
+ if (!hdrsearched) return 0;
+ // end of header search
+
+ // read from source
+ if (m_prebuffer ||
+ (((!unpacket.isValid() || audio_frames_avail < (int)((getFrameRate() * m_buffer_total)/1000.0)) || !videobs.avail()) &&
+ (!m_buf_memlimit || ((int)(audiobs.avail()+videobs.avail()) < m_buf_memlimit*8))))
+ {
+readagain_stream:
+ if (quit && *quit) return 0;
+ int lavail= (int)inbs.avail();
+
+ int ret=unpacket.unpacket(inbs);
+
+ inbs.compact();
+
+ if (ret)
+ {
+ if (!unpacket.isValid() && nsvbitstream_search > 8*1024*1024) // only scan for 8mb max
+ {
+ m_err="Error synching to NSV stream";
+ return -1;
+ }
+ if (unpacket.getEof())
+ {
+ if (!videobs.avail() && !video_frames_avail && (!aud_output || aud_output->isplaying() <= 0)) retval=-1;
+ // do nothing
+ }
+ else
+ {
+ char buf[8192] = {0};
+
+ size_t ret2=file_reader->read(buf,sizeof(buf));
+
+ if (ret2 == 0 && file_reader->iseof())
+ {
+ m_prebuffer=0;
+ unpacket.setEof(1);
+ }
+
+ if (file_reader->geterror())
+ {
+ m_err=file_reader->geterror();
+ return -1;
+ }
+
+ if (ret2 > 0)
+ {
+ nsvbitstream_search+= (int)ret2;
+ inbs.add(buf, (int)ret2);
+ goto readagain_stream;
+ }
+ }
+ }
+ else
+ {
+ nsvbitstream_search=0;
+ video_frames_avail++;
+ audio_frames_avail++;
+ nsv_type_to_string(unpacket.getAudFmt(),m_audio_type);
+ nsv_type_to_string(unpacket.getVidFmt(),m_video_type);
+
+ avg_framesize_cnt++;
+ avg_framesize_tot+=lavail- (int)((inbs.avail())/8);
+ if (avg_framesize_tot > 0x80000000)
+ {
+ avg_framesize_tot/=2;
+ avg_framesize_cnt/=2;
+ }
+
+ if (1)
+ {
+ //parse aux packets
+ while (auxbs.avail() >= 8*8)
+ {
+ size_t thislen = auxbs.getbits(32);
+ unsigned int thistype = auxbs.getbits(32);
+ if (thislen > auxbs.avail()/8) break;
+
+ if (thistype == NSV_MAKETYPE('S','U','B','T') ) ProcessSubtitleBlock(auxbs.getcurbyteptr(), (int)thislen);
+ else if (thistype == NSV_MAKETYPE('A','S','Y','N'))
+ {
+ //if (thislen == 0) // resyncpoint
+ {
+ audiobs.addint(-1);
+ audiobs.addint(framecnt+video_frames_avail);
+ }
+ //else // handle other form [todo]
+ //{
+ //}
+ }
+
+ auxbs.seek(thislen*8);
+ }
+ }
+ auxbs.clear();
+ }
+ }
+
+ if (unpacket.isValid() && !m_prebuffer && !audiobs.avail() && aud_output)
+ {
+ if (aud_output->isplaying()) aud_output->write(0,0);
+ else if (!unpacket.getEof())
+ {
+ m_prebuffer=m_pb_init_ur+(int)((video_frames_avail/getFrameRate())*1000.0);
+ if (m_prebuffer < m_pb_init_ur) m_prebuffer=m_pb_init_ur;
+ if (m_prebuffer > m_pb_init_ur*3) m_prebuffer=m_pb_init_ur*3;
+ retval=2;
+ }
+ }
+
+ if (video_frames_avail > (int)((m_prebuffer*getFrameRate())/1000.0))
+ {
+ m_bufstate=256;
+ m_prebuffer=0;
+ }
+ else
+ {
+ int a = (int) (video_frames_avail * 256.0 / ((m_prebuffer*getFrameRate())/1000.0));
+ if (a > 255) a=255;
+ if (a < 0) a=0;
+ m_bufstate=a;
+ if (m_out) m_out->notifyBufferState(a);
+ }
+
+ if (unpacket.isValid() && !vidout_ready && videobs.avail() && !m_prebuffer) // decode frame
+ {
+ if (!vid_decoder)
+ {
+ vid_flip=0;
+ vid_decoder = CreateVideoDecoder(unpacket.getWidth(),unpacket.getHeight(),
+ getFrameRate(),unpacket.getVidFmt(),&vid_flip,&vid_decoder_isnotnull);
+ }
+
+ int kf=0;
+ int l=videobs.getbits(32);
+ unsigned int local_vidout_type[3] = { 1, 0, 0 };
+ int ret=vid_decoder->decode(needkf,videobs.getcurbyteptr(),l,&vidout,local_vidout_type,&kf);
+ vidout_type = local_vidout_type[0];
+ if (ret == 0)
+ {
+
+ vidout_codec_width = local_vidout_type[1];
+ vidout_codec_height = local_vidout_type[2];
+ }
+
+ if (kf) needkf=0;
+ video_frames_avail--;
+ videobs.seek(l*8);
+ videobs.compact();
+
+ avg_framesize_cnt_v++;
+ avg_framesize_tot_v+=l;
+
+ if (needkf || seek_dumpframes > 0)
+ {
+ vidout=NULL;
+ if (seek_dumpframes>0) seek_dumpframes--;
+ }
+ else
+ {
+ if (!m_out_opened && vid_decoder_isnotnull)
+ {
+ m_out->extended(0x1008/*VIDUSER_SET_THREAD_SAFE*/, 1, 0);
+ unsigned int vidwidth = vidout_codec_width, vidheight = vidout_codec_height;
+ if (vidwidth && vidheight)
+ {
+ /* if the codec provided a width/height to use */
+ double aspect_adjust = (double)vidwidth / (double)vidheight / ((double)unpacket.getWidth() / (double)unpacket.getHeight());
+ if (m_out->open(vidwidth,vidheight,vid_flip,aspect * aspect_adjust,vidout_type))
+ {
+ m_err="Error opening video output";
+ return -1;
+ }
+ }
+ else
+ {
+ if (m_out->open(unpacket.getWidth(),unpacket.getHeight(),vid_flip,aspect,vidout_type))
+ {
+ m_err="Error opening video output";
+ return -1;
+ }
+ }
+ m_out_opened=1;
+ }
+
+ vidout_ready=1;
+ vidout_time = (unsigned int)(framecnt++ / getFrameRate() * 1000.0) + (unsigned int)m_avresync_time;
+ int offs=unpacket.getSyncOffset() + m_out->get_latency();
+
+ if ((int)vidout_time <= offs) vidout_time=0;
+ else vidout_time-=offs;
+
+ //drop the image if we're late
+ if(aud_output && vidout_time<aud_output->getpos()) {
+ vidout=0;
+ vidout_ready=0;
+ }
+ }
+
+ retval=1;
+ }
+
+ if ( ( audiobs.avail() && !m_prebuffer ) || m_again )
+ {
+ if (needkf)
+ {
+ int l=audiobs.getbits(32);
+ if (l == -1) audiobs.seek(32); // skip over 32 bits of our audiobs info
+ else
+ {
+ audiobs.seek(l*8);
+ audio_frames_avail--;
+ }
+
+ audiobs.compact();
+ }
+ else // decode some audio
+ {
+ char pcmbuf[16384] = {0};
+ int outbufl=512;
+ if (!aud_decoder)
+ {
+ aud_decoder=CreateAudioDecoder(unpacket.getAudFmt(),&aud_decoder_isnotnull,&aud_output);
+ if (aud_output && m_paused) aud_output->pause(1);
+ }
+
+ if (aud_output) outbufl=aud_output->canwrite();
+
+ if (outbufl)
+ {
+ unsigned int outfmt[8] = {0};
+
+ retval=1;
+ if (outbufl > sizeof(pcmbuf)) outbufl=sizeof(pcmbuf);
+
+ int inl=audiobs.getbits(32);
+
+ if (inl == -1)
+ {
+ int vidframe=audiobs.getbits(32);
+ if (aud_output)
+ {
+ ULONGLONG audpos = aud_output->getwritepos();
+ int vidpos = (int)(vidframe / getFrameRate() * 1000.0);
+
+ m_avresync_time = audpos-vidpos;
+ }
+ }
+ else
+ {
+ int ret=aud_decoder->decode(audiobs.getcurbyteptr(), inl, pcmbuf, &outbufl, outfmt);
+
+ if ( !outbufl ) m_again = 0;
+ else m_again =1;
+
+ if (ret < 1)
+ {
+ avg_framesize_cnt_a++;
+ avg_framesize_tot_a+=inl;
+ audiobs.seek(inl*8);
+ audiobs.compact();
+ audio_frames_avail--;
+ }
+ else audiobs.seek(-32);
+
+ if (outbufl || outfmt[0])
+ {
+ if (!aud_output)
+ {
+ aud_output=PCMOUT_CREATE(outfmt);
+ pcm_samplerate=outfmt[1];
+ if (!aud_output) aud_output = new NullAudioOutput;
+ aud_output->setvolume(m_volume);
+ aud_output->setpan(m_pan);
+ if (aud_output && m_paused) aud_output->pause(1);
+ }
+ if (outbufl)
+ {
+ if (seek_dumpaudiosamples)
+ {
+ int nch=outfmt[2];
+ int bps=outfmt[3];
+ int dump=seek_dumpaudiosamples*nch*(bps/8);
+ if (dump >= outbufl)
+ {
+ seek_dumpaudiosamples -= outbufl/nch/(bps/8);
+ }
+ else // partial write
+ {
+ aud_output->write(pcmbuf+dump,outbufl-dump);
+ seek_dumpaudiosamples=0;
+ }
+ }
+ else aud_output->write(pcmbuf,outbufl);
+ }
+ }
+ }
+ } // if can write
+ } //end of decode
+ } // audiobs avail
+
+ if (m_prebuffer)
+#ifdef _WIN32
+ Sleep(10);
+#else
+ usleep(10000);
+#endif
+
+ return retval;
+}
+
+Subtitles *NSVDecoder::insertSubtitlesItem(const char *language, const char *subfile) {
+ Subtitles *sub=new Subtitles(subfile);
+ m_subtitles.put(new SubtitlesItem(language, sub));
+ return sub;
+}
+
+Subtitles *NSVDecoder::findSubtitles(const char *language) {
+ for(int i=0;i<m_subtitles.getlen();i++) {
+ SubtitlesItem *s=m_subtitles.get(i);
+ if(!_stricmp(language,s->m_language) ) return s->m_subs;
+ }
+ return NULL;
+}
+
+const char *NSVDecoder::getSubLanguage(int index) {
+ if(index>=m_subtitles.getlen()) return NULL;
+ return m_subtitles.get(index)->m_language;
+} \ No newline at end of file
diff --git a/Src/nsv/nsvplay/nsvplay.dsp b/Src/nsv/nsvplay/nsvplay.dsp
new file mode 100644
index 00000000..4b49c0b1
--- /dev/null
+++ b/Src/nsv/nsvplay/nsvplay.dsp
@@ -0,0 +1,464 @@
+# Microsoft Developer Studio Project File - Name="nsvplay" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=nsvplay - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "nsvplay.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "nsvplay.mak" CFG="nsvplay - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "nsvplay - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "nsvplay - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "nsvplay - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "USE_ASM" /D "NO_LAYER12" /D "DLL_DECODER_SUPPORT" /D "BUILTIN_MP3_SUPPORT" /D "BUILTIN_VP3_SUPPORT" /D "SUBTITLES_READER" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ddraw.lib wsock32.lib vfw32.lib /nologo /subsystem:windows /machine:I386 /out:"c:\nsv\nsvplay.exe"
+
+!ELSEIF "$(CFG)" == "nsvplay - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "BUILTIN_VFW_SUPPORT" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "USE_ASM" /D "NO_LAYER12" /D "DLL_DECODER_SUPPORT" /D "BUILTIN_MP3_SUPPORT" /D "BUILTIN_VP3_SUPPORT" /D "SUBTITLES_READER" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ddraw.lib winmm.lib wsock32.lib vfw32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"c:\nsv\nsvplay.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "nsvplay - Win32 Release"
+# Name "nsvplay - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Group "audio"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\audiostub.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\audiostub.h
+# End Source File
+# End Group
+# Begin Group "vp3"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\vp32\include\duck_dxl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\vp3stub.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\vp3stub.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\vp32\lib\win32\Release\s_cpuid.lib
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\vp32\lib\win32\Release\s_dxv.lib
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\vp32\lib\win32\Release\s_sal.lib
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\vp32\lib\win32\Release\s_vp31d.lib
+# End Source File
+# End Group
+# Begin Group "mp3dec"
+
+# PROP Default_Filter ""
+# Begin Group "fhg"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\mp3dec\bitsequence.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\bitstream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\bitstream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\conceal.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\conceal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\crc16.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\crc16.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\giobase.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\huffdec.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\huffdec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\huffmanbitobj.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\huffmandecoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\huffmandecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\huffmantable.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\huffmantable.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\l3table.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\l3table.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mdct.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mdct.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp2decode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp2decode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3decode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3decode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3quant.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3quant.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3read.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3read.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3ssc.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3ssc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3sscdef.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3streaminfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3tools.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mp3tools.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mpeg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mpegbitstream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mpegbitstream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mpegheader.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mpegheader.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mpgadecoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\mpgadecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\polyphase.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\mp3dec\polyphase.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\mp3stub.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\mp3stub.h
+# End Source File
+# End Group
+# Begin Group "nsv"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\nsvbs.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\nsvlib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nsvlib.h
+# End Source File
+# End Group
+# Begin Group "jnetlib"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\jnetlib\asyncdns.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\asyncdns.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\connection.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\connection.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\httpget.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\httpget.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\jnetlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\netinc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\util.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jnetlib\util.h
+# End Source File
+# End Group
+# Begin Group "video"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\vid_ddraw.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\vid_ddraw.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\vid_overlay.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\vid_overlay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\video.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\video.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\about.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\decoders.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\nsvdecode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\nsvplay.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\readers.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\subtitles.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\wndmenu.h
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\main.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\subtitles.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\nsv_logo.bmp
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Src/nsv/nsvplay/nsvplay.dsw b/Src/nsv/nsvplay/nsvplay.dsw
new file mode 100644
index 00000000..2fafc97e
--- /dev/null
+++ b/Src/nsv/nsvplay/nsvplay.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "nsvplay"=.\nsvplay.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Src/nsv/nsvplay/nsvplay.rc b/Src/nsv/nsvplay/nsvplay.rc
new file mode 100644
index 00000000..43e1fde4
--- /dev/null
+++ b/Src/nsv/nsvplay/nsvplay.rc
@@ -0,0 +1,106 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUT DIALOG DISCARDABLE 0, 0, 184, 54
+STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About..."
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,105,33,50,14
+ CTEXT "Static",IDC_VERSION,74,7,103,8
+ CTEXT "Copyright (C) 2003 Nullsoft",IDC_STATIC,74,16,103,8
+ CONTROL 102,IDC_STATIC,"Static",SS_BITMAP,7,7,67,39
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_ABOUT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 177
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 47
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_BITMAP1 BITMAP DISCARDABLE "nsv_logo.bmp"
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/nsv/nsvplay/readers.cpp b/Src/nsv/nsvplay/readers.cpp
new file mode 100644
index 00000000..ff048e70
--- /dev/null
+++ b/Src/nsv/nsvplay/readers.cpp
@@ -0,0 +1,949 @@
+#include <windows.h>
+#include "main.h"
+#include <sys/stat.h>
+
+#ifdef NO_WASABI
+#include "../../jnetlib/httpget.h"
+api_httpreceiver *CreateGet()
+{
+ return new JNL_HTTPGet;
+}
+
+void ReleaseGet(api_httpreceiver *&get)
+{
+ delete (JNL_HTTPGet *)get;
+ get=0;
+}
+#else
+#include "../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include <api.h>
+#include <api/service/waservicefactory.h>
+#include "../../Winamp/in2.h"
+extern In_Module mod;
+
+waServiceFactory *httpFactory = 0;
+api_httpreceiver *CreateGet()
+{
+ api_httpreceiver *get = 0;
+ if (!httpFactory && mod.service)
+ httpFactory = mod.service->service_getServiceByGuid(httpreceiverGUID);
+
+ if (httpFactory)
+ get = (api_httpreceiver *)httpFactory->getInterface();
+
+ return get;
+}
+
+void ReleaseGet(api_httpreceiver *&get)
+{
+ if (!get)
+ return ;
+
+ if (!httpFactory && mod.service)
+ waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID);
+
+ if (httpFactory)
+ httpFactory->releaseInterface(get);
+
+ get = 0;
+}
+
+
+#endif
+#define MAX_MULTICONNECTS 8
+
+class HTTPReader : public IDataReader
+{
+public:
+ HTTPReader(const char *url);
+ ~HTTPReader();
+ size_t read(char *buf, size_t len);
+ bool iseof() { return !!m_eof; }
+ char *gettitle() { return m_title; }
+ char *geterror() { return m_err; }
+ bool canseek() { return m_content_length != 0xFFFFFFFF && m_accept_ranges && !m_meta_interval; }
+ int seek(unsigned __int64 newpos)
+ {
+ if (!canseek()) return 1;
+ doConnect((int)newpos);
+ return 0;
+ }
+unsigned __int64 getsize() { return m_content_length; }
+ char *getheader(char *header_name)
+ {
+ return m_get ? (char *)m_get->getheader(header_name) : NULL;
+ }
+private:
+
+ int serialconnect( int seekto , int timeout);
+ void doConnect(int seekto);
+ int getProxyInfo(const char *url, char *out);
+ char *m_url;
+ char *m_err;
+ char *m_title;
+ int m_eof;
+ int m_meta_init, m_meta_interval, m_meta_pos, m_meta_size, m_meta_buf_pos;
+ char m_meta_buf[4096];
+ int m_read_headers;
+ unsigned int m_content_length;
+ int m_accept_ranges;
+ int m_is_uvox;
+ int m_uvox_readpos;
+ int m_uvox_enough_bytes;
+ char proxybuf[8400], *proxy;
+
+ api_httpreceiver *m_get;
+
+ // this structure is allocated once, and freed once
+ struct
+ {
+ char *url; //pointers into m_url
+ // these two are only active temporarily.
+ api_httpreceiver *get;
+ char *error;
+ }
+ m_cons[MAX_MULTICONNECTS];
+ int m_numcons;
+ int m_newcons;
+ int m_serialized;
+ int m_mstimeout;
+ int m_contryptr;
+ int m_serialfailed;
+ int m_useaproxy;
+};
+
+HTTPReader::HTTPReader(const char *url)
+{
+ m_title = 0;
+ m_is_uvox = m_uvox_readpos = 0;
+ m_meta_init = m_meta_interval = m_meta_pos = m_meta_size = m_meta_buf_pos = 0;
+ m_meta_buf[0] = 0;
+ m_err = NULL;
+ m_eof = 0;
+ m_read_headers = 0;
+ m_content_length = 0xFFFFFFFF;
+ m_accept_ranges = 0;
+ m_get = NULL;
+ m_serialized = 0;
+ m_mstimeout = 0;
+ m_contryptr = 0;
+ m_newcons = 0;
+ m_serialfailed = 0;
+ m_useaproxy = 0;
+
+ // TCP multiconnect
+ // JF> using ; as a delimiter is vomit inducing and breaks a lot of other
+ // code. I petition we use <> to delimit, and I'm making it do that.
+
+ m_numcons = 0;
+ m_url = _strdup(url);
+
+ int allowproxy = 1;
+ char *tmpurl = m_url;
+ while (m_numcons < MAX_MULTICONNECTS)
+ {
+ char *next = strstr( tmpurl, "<>" );
+ if ( next ) *next = '\0';
+
+ if (tmpurl[0])
+ {
+ m_cons[m_numcons].error = NULL;
+ m_cons[m_numcons].get = NULL;
+ m_cons[m_numcons++].url = tmpurl;
+ if (!_strnicmp(tmpurl, "uvox:", 5)) allowproxy = 0;
+ if (!_strnicmp(tmpurl, "order://", 8))
+ {
+ char *p = tmpurl + 8;
+ // serialized mctp
+ m_serialized = 1;
+ m_numcons--;
+ m_mstimeout = atoi(p);
+ if ( m_mstimeout < 1 )
+ {
+ m_serialized = 0;
+ m_mstimeout = 0;
+ }
+ }
+ }
+
+ if (!next) break;
+
+ tmpurl = next + 2;
+ }
+
+ memset(proxybuf, 0, sizeof(proxybuf));
+ proxy = NULL;
+ if (allowproxy && getProxyInfo(url, proxybuf))
+ {
+ proxy = strstr(proxybuf, "http=");
+ if (!proxy) proxy = proxybuf;
+ else
+ {
+ proxy += 5;
+ char *tp = strstr(proxy, ";");
+ if (tp) *tp = 0;
+ }
+ }
+ m_is_uvox = 0;
+
+ if ( m_serialized && m_numcons > 1 ) // sanity check
+ {
+ int rval = 0, i;
+ m_newcons = 1;
+ // walk the list, set the url such that m_cons[0].url points to each item. try to connect
+ // serialconnect returns error codes -1 on error, 0 on timeout, 1 on successfull connect
+ for ( i = 0; i < m_numcons; i++ )
+ {
+ if ( i )
+ {
+ m_cons[0].url = m_cons[i].url;
+ }
+ rval = serialconnect(0, m_mstimeout);
+ if ( rval == 1 ) break;
+ }
+ if ( rval < 1 )
+ {
+ // we didnt get a connection so...
+ m_serialfailed = 1;
+ }
+
+ }
+ else
+ doConnect(0);
+}
+
+
+void HTTPReader::doConnect(int seekto)
+{
+ ReleaseGet(m_get);
+ m_uvox_readpos = 0;
+
+ m_eof = 0;
+
+
+ int i;
+ for (i = 0; i < m_numcons; i++ )
+ {
+ free(m_cons[i].error);
+ m_cons[i].error = NULL;
+ ReleaseGet(m_cons[i].get);
+ m_cons[i].get = CreateGet();
+ if (!m_cons[i].get)
+ break;
+ m_cons[i].get->open(API_DNS_AUTODNS, 65536, (proxy && proxy[0]) ? proxy : NULL);
+#ifdef WINAMP_PLUGIN
+ m_cons[i].get->addheader("User-Agent:Winamp NSV Player/5.12 (ultravox/2.0)");
+#else
+ # ifdef WINAMPX
+ m_cons[i].get->addheader("User-Agent:" UNAGI_USER_AGENT " (ultravox/2.0)");
+# else
+ m_cons[i].get->addheader("User-Agent:NSV Player/0.0 (ultravox/2.0)");
+# endif
+#endif
+ m_cons[i].get->addheader("Accept:*/*");
+ m_cons[i].get->addheader("Connection:close");
+ m_cons[i].get->addheader("Ultravox-transport-type: TCP");
+
+ if (seekto)
+ {
+ char buf[64] = {0};
+ wsprintfA(buf, "Range:bytes=%d-", seekto);
+ m_cons[i].get->addheader(buf);
+ }
+ else
+ m_cons[i].get->addheader("icy-metadata:1");
+
+ m_cons[i].get->connect(m_cons[i].url, !!seekto);
+ }
+ m_uvox_enough_bytes = 1;
+}
+
+HTTPReader::~HTTPReader()
+{
+ ReleaseGet(m_get);
+ free(m_title);
+ free(m_err);
+ free(m_url);
+
+ int i;
+ for (i = 0; i < m_numcons; i++)
+ {
+ ReleaseGet(m_cons[i].get);
+ free(m_cons[i].error);
+ }
+}
+
+int HTTPReader::serialconnect(int seekto , int timeout)
+{
+
+ ReleaseGet(m_get);
+ m_uvox_readpos = 0;
+
+ m_eof = 0;
+
+ int64_t mythen, mynow , myref;
+ LARGE_INTEGER then, now, ref;
+
+
+ QueryPerformanceFrequency( &ref);
+ myref = ref.QuadPart;
+
+
+ QueryPerformanceCounter( &then );
+ mythen = then.QuadPart;
+
+
+ int timer = 0;
+
+ int i = 0;
+
+ {
+ ReleaseGet(m_cons[i].get);
+ m_cons[i].get = CreateGet();
+ if (m_cons[i].get == NULL)
+ return 0;
+ m_cons[i].get->open(API_DNS_AUTODNS, 65536, (proxy && proxy[0]) ? proxy : NULL);
+#ifdef WINAMP_PLUGIN
+ m_cons[i].get->addheader("User-Agent:Winamp NSV Player/5.12 (ultravox/2.0)");
+#else
+# ifdef WINAMPX
+ m_cons[i].get->addheader("User-Agent:" UNAGI_USER_AGENT " (ultravox/2.0)");
+# else
+ m_cons[i].get->addheader("User-Agent:NSV Player/0.0 (ultravox/2.0)");
+# endif
+#endif
+ m_cons[i].get->addheader("Accept:*/*");
+ m_cons[i].get->addheader("Connection:close");
+ m_cons[i].get->addheader("Ultravox-transport-type: TCP");
+
+ if (seekto)
+ {
+ char buf[64] = {0};
+ wsprintfA(buf, "Range:bytes=%d-", seekto);
+ m_cons[i].get->addheader(buf);
+ }
+ else m_cons[i].get->addheader("icy-metadata:1");
+
+ m_cons[i].get->connect(m_cons[i].url, !!seekto);
+ }
+ m_uvox_enough_bytes = 1;
+
+ int ret, status;
+
+ if (!m_get)
+ {
+ if (m_err) return 0;
+ int i;
+ int found = 0;
+ i = 0;
+ while ( timer < timeout )
+ {
+ if (!m_cons[i].get) return 0;
+ found = 1;
+
+ QueryPerformanceCounter( &now );
+ mynow = now.QuadPart;
+
+ float profiletime = (float)(mynow - mythen);
+ profiletime /= myref;
+ profiletime *= 1000.0;
+ timer = (int) profiletime;
+
+ ret = m_cons[i].get->run();
+ status = m_cons[i].get->get_status();
+
+ if (ret < 0 || status < 0)
+ {
+ const char *t = m_cons[i].get->geterrorstr();
+ if (t)
+ {}
+ ReleaseGet(m_cons[i].get);
+ break;
+ }
+
+ if ( status > 0 )
+ {
+ int code = m_cons[i].get->getreplycode();
+ if ( code < 200 || code > 299 )
+ {
+ ReleaseGet(m_cons[i].get);
+ //wsprintf( m_cons[i].error, "Error: Server returned %d", code );
+ break;
+ }
+ else
+ {
+ // we're in good shape, make our getter current, and delete all the gay ones
+ ReleaseGet(m_get); // just in case, probably zero anyway
+ m_get = m_cons[i].get;
+ m_cons[i].get = NULL;
+
+ // trash i here, but we are breaking anyway :)
+ /* for (i = 0; i < m_numcons; i++)
+ {
+ delete m_cons[i].get;
+ m_cons[i].get = NULL;
+ free( m_cons[i].error );
+ m_cons[i].error = NULL;
+ }*/
+ break;
+ }
+ }
+#ifdef _WIN32
+ Sleep(1);
+#else
+ usleep(1000);
+#endif
+ } // while
+ if ( timer > timeout )
+ {
+ ReleaseGet(m_cons[i].get);
+ ReleaseGet(m_get);
+ return 0;
+ }
+
+
+ if (!m_get)
+ {
+ return 0;
+ }
+ }
+ if ( m_get ) return 1;
+ else return 0;
+}
+
+
+size_t HTTPReader::read(char *buffer, size_t len)
+{
+ int ret, status;
+
+ if (!m_get)
+ {
+ if (m_err) return 0;
+ int i;
+ int found = 0;
+ for (i = 0; !m_get && i < m_numcons; i++)
+ {
+ if (!m_cons[i].get) continue;
+ found = 1;
+
+ ret = m_cons[i].get->run();
+ status = m_cons[i].get->get_status();
+
+ if (ret < 0 || status < 0)
+ {
+ const char *t = m_cons[i].get->geterrorstr();
+ if (t)
+ {
+ free(m_cons[i].error);
+ m_cons[i].error = _strdup( t );
+ }
+ ReleaseGet(m_cons[i].get);
+
+ }
+
+ if ( status > 0 )
+ {
+ int code = m_cons[i].get->getreplycode();
+ if ( code < 200 || code > 299 )
+ {
+ ReleaseGet(m_cons[i].get);
+ m_cons[i].get = NULL;
+
+ free(m_cons[i].error);
+ m_cons[i].error = (char *)malloc( 100 );
+ wsprintfA( m_cons[i].error, "Error: Server returned %d", code );
+ }
+ else
+ {
+ // we're in good shape, make our getter current, and delete all the gay ones
+ ReleaseGet(m_get); // just in case, probably zero anyway
+ m_get = m_cons[i].get;
+ m_cons[i].get = NULL;
+
+ // trash i here, but we are breaking anyway :)
+ for (i = 0; i < m_numcons; i++)
+ {
+ ReleaseGet(m_cons[i].get);
+ free( m_cons[i].error );
+ m_cons[i].error = NULL;
+ }
+ break; // exit loop of connections
+ }
+ }
+ } // loop of connections
+
+ if (!found) // out of attempted connections heh
+ {
+ free( m_err );
+ if (m_numcons > 1)
+ {
+ size_t size = 0;
+ for (i = 0; i < m_numcons; i++)
+ if ( m_cons[i].error ) size += strlen( m_cons[i].error ) + 1;
+
+ m_err = (char *)malloc(size + 100);
+ wsprintfA( m_err, "No Valid Multiconnect URLs (%d);", m_numcons );
+ for (i = 0; i < m_numcons; i++)
+ {
+ strcat( m_err, m_cons[i].error );
+ strcat( m_err, ";" );
+ free(m_cons[i].error);
+ m_cons[i].error = NULL;
+
+ }
+ }
+ else
+ {
+ m_err = m_cons[0].error;
+ m_cons[0].error = NULL;
+ if (!m_err) m_err = _strdup("Connection error (Invalid URL?)");
+ }
+ }
+ if (!m_get) return 0;
+ }
+
+ ret = m_get->run();
+ status = m_get->get_status();
+
+ if (ret > 0 && (!m_get->bytes_available() || !m_uvox_enough_bytes) && status > 1)
+ {
+ m_eof = 1;
+ }
+
+ if (ret < 0 || status < 0)
+ {
+ const char *t = m_get->geterrorstr();
+ if (t)
+ {
+ free( m_err );
+ m_err = (char *)malloc( strlen( t) + 16 );
+ wsprintfA( m_err, "Error: %s", t );
+ return 0;
+ }
+ }
+
+ if (status > 0)
+ {
+ if (!m_read_headers)
+ {
+ if (status > 1)
+ {
+ const char *v = m_get->getheader("Content-Length");
+ if (v) m_content_length = atoi(v);
+ v = m_get->getheader("Accept-Ranges");
+ if (v) while (v && *v)
+ {
+ if (!_strnicmp(v, "bytes", 5))
+ {
+ m_accept_ranges = 1;
+ break;
+ }
+ v++;
+ }
+ v = m_get->getheader("icy-metaint");
+ if (v)
+ {
+ m_meta_interval = atoi(v);
+ }
+ if (!m_title)
+ {
+ v = m_get->getheader("icy-name");
+ if (v)
+ m_title = _strdup(v);
+ }
+#ifdef WINAMP_PLUGIN
+ extern void process_url(char *url);
+ v = m_get->getheader("icy-url");
+ if (v && !strstr(v, "shoutcast.com"))
+ {
+ char *p = (char *)v; while (p && *p && *p == ' ') p++;
+ process_url(p);
+ }
+#endif
+
+ v = m_get->getheader("content-type");
+ if (v && !_stricmp(v, "misc/ultravox"))
+ {
+ v = m_get->getheader("ultravox-max-msg");
+ if (v) m_is_uvox = atoi(v);
+ if (!m_is_uvox) m_is_uvox = 16000;
+ }
+ if (!m_title)
+ {
+ v = m_get->getheader("content-disposition");
+ if (v) v = strstr(v, "filename=");
+ if (v)
+ {
+ m_title = _strdup(v + 9);
+ }
+ }
+ m_read_headers = 1;
+ }
+ }
+
+ size_t l = m_get->bytes_available();
+ if (m_is_uvox)
+ {
+ again:
+ if (l >= 6)
+ {
+ unsigned char buf[32768*2] = {0};
+ m_get->peek_bytes((char *)buf, 6);
+ if (buf[0] != 0x5A)
+ {
+ l--;
+ m_get->get_bytes((char *)buf, 1);
+ goto again;
+ }
+ int resqos = buf[1];
+ int classtype = (buf[2] << 8) | buf[3];
+ int msglen = (buf[4] << 8) | buf[5];
+ if (msglen > m_is_uvox) // length is too long
+ {
+ m_get->get_bytes((char *)buf, 1);
+ l--;
+ goto again;
+ }
+ if (msglen + 7 <= (int)l)
+ {
+ m_uvox_enough_bytes = 1;
+
+ m_get->peek_bytes((char *)buf, msglen + 7);
+ if (buf[msglen + 6])
+ {
+ m_get->get_bytes((char *)buf, 1);
+ l--;
+ goto again;
+ }
+ if (classtype == 0x7777) // take any data for now, ignore all other frames
+ {
+ l = msglen - m_uvox_readpos;
+ if (l > len) l = len;
+ memcpy(buffer, buf + 6 + m_uvox_readpos, l);
+ m_uvox_readpos += (int)l;
+
+ if (m_uvox_readpos >= msglen)
+ {
+ m_uvox_readpos = 0;
+ m_get->get_bytes((char *)buf, msglen + 7);
+
+ }
+ return l;
+
+#ifdef WINAMP_PLUGIN
+
+ }
+ else if ( classtype == 0x3001 )
+ {
+ extern void process_metadata(char *buf, int size);
+ m_get->get_bytes((char *)buf, msglen + 7);
+ process_metadata((char*)buf + 6, msglen + 1);
+#endif
+
+ }
+ else
+ {
+ m_get->get_bytes((char *)buf, msglen + 7);
+ }
+ }
+ else
+ {
+ m_uvox_enough_bytes = 0;
+ }
+ }
+ return 0;
+ }
+ else
+ {
+ if (l > len) l = len;
+ m_get->get_bytes(buffer, (int)l);
+
+ if (m_meta_interval)
+ {
+ int x = (int)l;
+ unsigned char *buf = (unsigned char *)buffer;
+ if (m_meta_size) // already in meta block
+ {
+ int len = min(x, m_meta_size - m_meta_buf_pos);
+
+ memcpy(m_meta_buf + m_meta_buf_pos, buf, len);
+ m_meta_buf_pos += len;
+
+ if (m_meta_buf_pos == m_meta_size)
+ {
+ // if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size);
+ m_meta_buf_pos = 0;
+ m_meta_size = 0;
+ m_meta_pos = 0;
+ }
+
+ x -= len;
+ if (x) memcpy(buf, buf + len, x);
+ }
+ else if (m_meta_pos + x > m_meta_interval) // block contains meta data somewhere in it, and we're not alreayd reading a block
+ {
+ int start_offs = m_meta_interval - m_meta_pos;
+ int len;
+ m_meta_size = ((unsigned char *)buf)[start_offs] * 16;
+
+ len = min(x - start_offs - 1, m_meta_size);
+
+ if (len) memcpy(m_meta_buf, buf + start_offs + 1, len);
+ m_meta_buf_pos = len;
+
+ if (m_meta_buf_pos == m_meta_size) // full read of metadata successful
+ {
+ x -= m_meta_size + 1;
+ if (x > start_offs) memcpy(buf + start_offs, buf + start_offs + 1 + m_meta_size, x - start_offs);
+#ifdef WINAMP_PLUGIN
+ extern void process_metadata(char *buf, int size);
+ process_metadata(m_meta_buf, m_meta_size);
+#endif
+ //if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size);
+ m_meta_buf_pos = 0;
+ m_meta_pos = -start_offs;
+ m_meta_size = 0;
+ }
+ else
+ {
+ x = start_offs; // otherwise, there's only the first block of data
+ }
+ }
+ if (x > 0)
+ {
+ m_meta_pos += x;
+ }
+ l = x;
+ } // end of poopie metadata
+ } // !uvox
+
+#if 0
+ {
+ FILE *fh = fopen("c:\\dump.nsv", "ab");
+ fwrite(buffer, 1, l, fh);
+ fclose(fh);
+ }
+#endif
+
+ return l;
+ }
+ return 0;
+}
+
+static void parseURL(char *url, char *lp, char *host, int *port, char *req)
+{
+ char *p, *np;
+ /* if (_strnicmp(url,"http://",4) &&
+ _strnicmp(url,"icy://",6) &&
+ _strnicmp(url,"sc://",6) &&
+ _strnicmp(url,"shoutcast://",12)) return;
+ */
+ np = p = strstr(url, "://");
+ if (!np) np = (char*)url;
+ else np += 3;
+ if (!p) p = (char*)url;
+ else p += 3;
+
+ while (np && *np != '/' && *np) *np++;
+ if (np && *np)
+ {
+ lstrcpynA(req, np, 2048);
+ *np++ = 0;
+ }
+ else strcpy(req, "/");
+ np = p;
+ while (np && *np != '@' && *np) np++;
+ if (np && *np)
+ {
+ *np++ = 0;
+ lstrcpynA(lp, p, 256);
+ p = np;
+ }
+ else lp[0] = 0;
+ np = p;
+ while (np && *np != ':' && *np) np++;
+ if (*np)
+ {
+ *np++ = 0;
+ *port = atoi(np);
+ }
+ else *port = 80;
+ lstrcpynA(host, p, 256);
+}
+
+int HTTPReader::getProxyInfo(const char *url, char *out)
+{
+#ifndef WINAMPX
+ char INI_FILE[MAX_PATH] = {0};
+ char *p;
+ GetModuleFileNameA(NULL, INI_FILE, sizeof(INI_FILE));
+ p = INI_FILE + strlen(INI_FILE);
+ while (p >= INI_FILE && *p != '.') p--;
+ strcpy(++p, "ini");
+ GetPrivateProfileStringA("Winamp", "proxy", "", out, 8192, INI_FILE);
+ return !!out[0];
+#else
+ DWORD v = 0;
+ HKEY hKey;
+
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\AOL\\Unagi", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ DWORD l = 4;
+ DWORD t;
+ if (RegQueryValueEx(hKey, "ProxyEnable", NULL, &t, (unsigned char *)&v, &l) == ERROR_SUCCESS && t == REG_DWORD)
+ {
+ if ( v != 2 )
+ {
+ l = 8192;
+ if (RegQueryValueEx(hKey, "ProxyServer", NULL, &t, (unsigned char *)out, &l ) != ERROR_SUCCESS || t != REG_SZ)
+ {
+ v = 0;
+ *out = 0;
+ }
+ }
+ else return 0;
+ }
+ else v = 0;
+ out[512 - 1] = 0;
+ RegCloseKey(hKey);
+ }
+ if ( !v && m_useaproxy )
+ {
+ char blah[8192] = "";
+ lstrcpyn(blah, url, 8192);
+ char plp[512] = {0};
+ char phost[512] = {0};
+ int pport = 80;
+ char pthereq[1024] = {0};
+ parseURL(blah, plp, phost, &pport, pthereq);
+ v = ResolvProxyFromURL(url, phost, out);
+ if ( v < 0 ) v = 0; // error getting proxy
+ }
+ if ( v > 0)
+ {
+ char prox[1024] = {0};
+ wsprintf(prox, "PROXY: %s", out);
+ SendMetadata(prox, 1);
+ }
+ return v;
+#endif
+}
+
+
+
+class Win32FileReader : public IDataReader
+{
+public:
+ Win32FileReader(HANDLE file) { m_hFile = file; m_eof = 0; m_err = NULL; }
+ ~Win32FileReader() { CloseHandle(m_hFile); }
+ size_t read(char *buf, size_t len)
+ {
+ DWORD ob = 0;
+ if (!len) return 0;
+ if (!ReadFile(m_hFile, buf, (DWORD)len, &ob, NULL))
+ {
+ m_err = "Error calling ReadFile()!";
+ return 0;
+ }
+ else if (!ob) m_eof = true;
+ return ob;
+ }
+bool iseof() { return m_eof; }
+ bool canseek() { return 1; }
+ int seek(uint64_t newpos)
+ {
+ LARGE_INTEGER li;
+ li.QuadPart = newpos;
+ li.LowPart = SetFilePointer (m_hFile, li.LowPart, &li.HighPart, SEEK_SET);
+
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart== ~0;
+ }
+
+ uint64_t getsize()
+ {
+ LARGE_INTEGER position;
+ position.QuadPart=0;
+ position.LowPart = GetFileSize(m_hFile, (LPDWORD)&position.HighPart);
+
+ if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
+ return INVALID_FILE_SIZE;
+ else
+ return position.QuadPart;
+ }
+
+ char *geterror() { return m_err; }
+private:
+ HANDLE m_hFile;
+ bool m_eof;
+ char *m_err;
+};
+
+
+#define VAR_TO_FPOS(fpos, var) (fpos) = (var)
+#define FPOS_TO_VAR(fpos, typed, var) (var) = (typed)(fpos)
+class FileReader : public IDataReader
+{
+public:
+ FileReader(FILE *file) { fp = file; m_err = NULL; }
+ ~FileReader() { fclose(fp); }
+ size_t read(char *buf, size_t len)
+ {
+ size_t ob;
+ if (!len) return 0;
+ ob = fread(buf, 1, len, fp);
+ if (!ob && ferror(fp))
+ {
+ m_err = "Error calling fread()!";
+ return 0;
+ }
+ return ob;
+ }
+ bool iseof() { return !!feof(fp); }
+ bool canseek() { return 1; }
+ int seek(uint64_t newpos)
+ {
+ fpos_t pos= newpos;
+ VAR_TO_FPOS(pos, newpos);
+ return fsetpos(fp, &pos);
+ }
+
+ unsigned __int64 getsize()
+ {
+ struct stat s;
+ if (fstat(fileno(fp), &s) < 0)
+ {
+ m_err = "Error calling fread()!";
+ return 0;
+ }
+ return s.st_size;
+ }
+
+ char *geterror() { return m_err; }
+private:
+ FILE *fp;
+ char *m_err;
+};
+
+
+IDataReader *CreateReader(const char *url)
+{
+ if (strstr(url, "://")) return new HTTPReader(url);
+#ifdef _WIN32
+ HANDLE hFile = CreateFileA(url, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ return new Win32FileReader(hFile);
+#else
+ FILE *fp = fopen(url, "r");
+ if (fp)
+ return new FileReader(fp);
+#endif
+ return NULL;
+}
+
diff --git a/Src/nsv/nsvplay/resource.h b/Src/nsv/nsvplay/resource.h
new file mode 100644
index 00000000..57b058a7
--- /dev/null
+++ b/Src/nsv/nsvplay/resource.h
@@ -0,0 +1,18 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by nsvplay.rc
+//
+#define IDD_ABOUT 101
+#define IDB_BITMAP1 102
+#define IDC_VERSION 1000
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 103
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/nsv/nsvplay/subtitles.cpp b/Src/nsv/nsvplay/subtitles.cpp
new file mode 100644
index 00000000..e599bf14
--- /dev/null
+++ b/Src/nsv/nsvplay/subtitles.cpp
@@ -0,0 +1,215 @@
+#include "subtitles.h"
+
+Subtitles::Subtitles(const char *filename)
+{
+ m_frame_based=0;
+ m_last_sub=-1;
+ m_font_size_mod=0;
+
+#ifdef SUBTITLES_READER
+ if(!filename) return;
+
+ IDataReader *file_reader=CreateReader(filename);
+ if(file_reader) {
+ char *text=NULL;
+ int textpos=0;
+ int allocsize=0;
+ char buf[1024] = {0};
+ unsigned aborttime=GetTickCount()+20000;
+ for (;;)
+ {
+ int l=file_reader->read(buf,1024);
+ if (l <= 0)
+ {
+ if (file_reader->iseof()) break;
+ if (file_reader->geterror() || GetTickCount() > aborttime)
+ {
+ free(text);
+ return;
+ }
+ Sleep(100);
+ }
+ else
+ {
+ if (textpos+l+1 >= allocsize)
+ {
+ allocsize = textpos+l+1+8192;
+ text=(char*)realloc(text,allocsize);
+ }
+ memcpy(text+textpos,buf,l);
+ textpos+=l;
+ }
+ }
+ if (text) {
+ text[textpos]=0;
+ //fucko: check for case
+ if(strstr(filename,".srt")) decodeSrtFile(text);
+ else if(strstr(filename,".sub")) decodeSubFile(text);
+ free(text);
+ }
+ }
+ delete(file_reader);
+#endif
+}
+
+#ifdef SUBTITLES_READER
+void Subtitles::decodeSrtFile(char *text) {
+ // parse subtitle file (.srt format)
+ char *p=text;
+
+ //for(int i=0;;i++) {
+ while(1) {
+ unsigned int time_start,time_end;
+
+ // parse title nb
+ char *p2=p;
+ while(*p2 && *p2!='\n') p2++;
+ *p2++=0;
+ //if(atoi(p)!=i+1) break;
+ if(atoi(p)<=0) break;
+
+ // parse start time
+ p=p2;
+ while(*p2 && *p2!=' ') p2++;
+ *p2++=0;
+ time_start=getTimeFromSrtText(p);
+
+ // parse "-->"
+ while(*p2 && *p2!=' ') p2++;
+ p2++;
+
+ // parse end time
+ p=p2;
+ while(*p2 && *p2!='\n') p2++;
+ *p2++=0;
+ time_end=getTimeFromSrtText(p);
+
+ // parse text
+ p=p2;
+ while(*p2 && !(*p2=='\r' || *p=='\n')) {
+ while(*p2 && *p2!='\n') p2++;
+ p2++;
+ }
+ *p2++=0;
+
+ //remove trailing CR
+ {
+ int l=strlen(p);
+ if(l) {
+ if(p[l-1]=='\r' || p[l-1]=='\n') p[l-1]=0;
+ }
+ }
+
+ m_subs.put(new SubsItem(time_start,time_end,p));
+
+ if(*p2=='\n') p2++;
+ p=p2;
+ }
+ m_frame_based=0;
+}
+
+unsigned int Subtitles::getTimeFromSrtText(const char *text) {
+ int hours,mins,secs,mills;
+ const char *p=text;
+ hours=atoi(p);
+ while(*p && *p!=':') p++;
+ p++;
+ mins=atoi(p);
+ while(*p && *p!=':') p++;
+ p++;
+ secs=atoi(p);
+ while(*p && *p!=',') p++;
+ p++;
+ mills=atoi(p);
+ return mills+(secs*1000)+(mins*60000)+(hours*60*60000);
+}
+
+void Subtitles::decodeSubFile(char *text)
+{
+ char *p=text;
+ while(*p=='{') {
+ int framestart,frameend;
+ p++;
+ char *p2=p;
+ while(*p2 && *p2!='}') p2++;
+ *p2++=0;
+ framestart=atoi(p);
+
+ p2+=1;
+ p=p2;
+ while(*p2 && *p2!='}') p2++;
+ *p2++=0;
+ frameend=atoi(p);
+
+ p=p2;
+ while(*p2 && *p2!='\r' && *p2!='\n') {
+ //replace pipes with CR
+ if(*p2=='|') *p2='\n';
+ p2++;
+ }
+ *p2++=0;
+
+ m_subs.put(new SubsItem(framestart,frameend,p));
+
+ if(*p2=='\n') p2++;
+ p=p2;
+ }
+ m_frame_based=1;
+}
+#endif
+
+SubsItem *Subtitles::getSubtitle(unsigned int time, unsigned int frame)
+{
+ unsigned int ref=m_frame_based?frame:time;
+
+ //check with lastsub
+ if(m_last_sub!=-1) {
+ SubsItem *item=m_subs.get(m_last_sub);
+ if(ref>=item->timestart && ref<=item->timeend)
+ {
+ item->fontSize=item->origFontSize+m_font_size_mod;
+ return item;
+ }
+ SubsItem *item2=m_subs.get(m_last_sub+1);
+ if(item2) {
+ if(ref>=item->timeend && ref<=item2->timestart) return NULL;
+ if(ref>=item2->timestart && ref<=item2->timeend) {
+ m_last_sub++;
+ item2->fontSize=item2->origFontSize+m_font_size_mod;
+ return item2;
+ }
+ }
+ }
+
+ int l= (int)m_subs.getlen();
+ for(int i=0;i<l;i++) {
+ SubsItem *item=m_subs.get(i);
+ if(ref<item->timestart) break;
+ if(ref>=item->timestart && ref<=item->timeend) {
+ m_last_sub=i;
+ item->fontSize=item->origFontSize+m_font_size_mod;
+ return item;
+ }
+ }
+ m_last_sub=-1;
+ return NULL;
+}
+
+void Subtitles::addSubtitlePacket(SUBTITLE_INFO *sti)
+{
+ m_frame_based=1; //FUCKO: put this in subsitem struct
+ SubsItem *i=new SubsItem(sti->start_frame,sti->end_frame,sti->utf8_text);
+ i->xPos=sti->xPos;
+ i->yPos=sti->yPos;
+ i->colorBlue=sti->colorBlue;
+ i->colorGreen=sti->colorGreen;
+ i->colorRed=sti->colorRed;
+ i->extraDataSize=sti->extraDataSize;
+ i->origFontSize=sti->fontSize;
+ if(sti->extraDataSize) {
+ i->extraData=malloc(sti->extraDataSize);
+ memcpy((void *)i->extraData,sti->extraData,sti->extraDataSize);
+ }
+ i->muxed_subtitle=1;
+ m_subs.put(i);
+} \ No newline at end of file
diff --git a/Src/nsv/nsvplay/subtitles.h b/Src/nsv/nsvplay/subtitles.h
new file mode 100644
index 00000000..f4951a23
--- /dev/null
+++ b/Src/nsv/nsvplay/subtitles.h
@@ -0,0 +1,62 @@
+#ifndef NSVPLAY_SUBTITLES_H
+#define NSVPLAY_SUBTITLES_H
+
+#include "main.h"
+#include "../nsvbs.h"
+
+class SubsItem {
+public:
+ SubsItem(unsigned int ptimestart, unsigned int ptimeend, const char *ptext) :
+ timestart(ptimestart) , timeend(ptimeend) {
+ text=_strdup(ptext);
+ xPos=128;
+ yPos=255;
+ colorRed=colorGreen=colorBlue=0xff;
+ extraDataSize=0;
+ extraData=0;
+ muxed_subtitle=0;
+ fontSize=origFontSize=0;
+ }
+ ~SubsItem() {
+ free((void*)text);
+ if(extraDataSize) free((void *)extraData);
+ }
+
+ unsigned int timestart;
+ unsigned int timeend;
+ const char *text;
+
+ unsigned char xPos, yPos;
+ unsigned char colorRed, colorGreen, colorBlue;
+ int extraDataSize;
+ const void *extraData;
+
+ int muxed_subtitle; //so we free it when we seek/display
+
+ int fontSize;
+
+ int origFontSize;
+};
+
+class Subtitles {
+public:
+ Subtitles(const char *filename);
+
+ SubsItem *getSubtitle(unsigned int time, unsigned int frame); // time in ms
+ void addSubtitlePacket(SUBTITLE_INFO *sti);
+
+ void setFontSizeModifier(int size) { m_font_size_mod=size; }
+
+private:
+ void decodeSrtFile(char *text);
+ unsigned int getTimeFromSrtText(const char *text);
+
+ void decodeSubFile(char *text);
+
+ ClassList<SubsItem> m_subs;
+ int m_frame_based;
+ int m_last_sub;
+ int m_font_size_mod;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nsv/nsvplay/vid_ddraw.cpp b/Src/nsv/nsvplay/vid_ddraw.cpp
new file mode 100644
index 00000000..b4c0af68
--- /dev/null
+++ b/Src/nsv/nsvplay/vid_ddraw.cpp
@@ -0,0 +1,865 @@
+#include "video.h"
+#include <multimon.h>
+#include "subtitles.h"
+
+#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
+
+DDrawVideoOutput::DDrawVideoOutput() {
+ lpDD=NULL;
+ lpddsOverlay=NULL;
+ lastresizerect.bottom=0;
+ lastresizerect.top=0;
+ lastresizerect.left=0;
+ lastresizerect.right=0;
+
+ lpddsPrimary=NULL;
+ lpddsClipper=NULL;
+ lpddsSTTemp=NULL;
+ is_fullscreen=0;
+ m_parent=NULL;
+ initing=false;
+ needchange=0;
+ m_palette=NULL;
+ m_lastsubtitle=NULL;
+ sttmp_w=sttmp_h=0;
+ subFont=NULL;
+ m_sub_needremeasure=0;
+ m_fontsize=0;
+ memset(&winRect,0,sizeof(winRect));
+}
+
+DDrawVideoOutput::~DDrawVideoOutput() {
+// LPDIRECTDRAWSURFACE o=lpddsOverlay;
+ lpddsOverlay=NULL;
+// if(o) o->Release();
+ // if (lpddsSTTemp) lpddsSTTemp->Release();
+ //if(lpddsPrimary) lpddsPrimary->Release();
+// if(lpddsClipper) lpddsClipper->Release();
+ if (lpDD) lpDD->Release(); // BU added NULL check in response to talkback
+ if(subFont) DeleteObject(subFont);
+ if (is_fullscreen) removeFullScreen();
+}
+
+void DDrawVideoOutput::drawSubtitle(SubsItem *item)
+{
+ m_lastsubtitle=item;
+ m_sub_needremeasure=1;
+}
+
+int DDrawVideoOutput::create(VideoOutput *parent, int w, int h, unsigned int ptype, int flipit, double aspectratio) {
+ m_lastsubtitle=NULL;
+ type=ptype;
+ width=w;
+ height=h;
+ flip=flipit;
+ m_parent=parent;
+
+ initing=true;
+ HWND hwnd=parent->getHwnd();
+
+ if (lpDD) lpDD->Release();
+ lpDD=NULL;
+
+ update_monitor_coords(parent);
+
+ if(!m_found_devguid) DirectDrawCreate(NULL,&lpDD,NULL);
+ else DirectDrawCreate(&m_devguid,&lpDD,NULL);
+
+ if(!lpDD) {
+ initing=false;
+ return 0;
+ }
+
+ lpDD->SetCooperativeLevel(hwnd,DDSCL_NOWINDOWCHANGES|DDSCL_NORMAL);
+
+ DDSURFACEDESC ddsd;
+ INIT_DIRECTDRAW_STRUCT(ddsd);
+ ddsd.dwFlags = DDSD_CAPS;
+ ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ HRESULT ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL );
+
+ HRESULT v=-1;
+ DDSURFACEDESC DDsd={sizeof(DDsd),};
+ lpddsPrimary->GetSurfaceDesc(&ddsd);
+ DDsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; //create the surface at screen depth
+ DDsd.dwWidth=w;
+ DDsd.dwHeight=h;
+ DDsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
+ if (parent->vid_ddraw) v=lpDD->CreateSurface(&DDsd, &lpddsOverlay, NULL);
+ if(!parent->vid_ddraw || FAILED(v)) {
+ // fall back to system memory if video mem doesn't work
+ DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
+ v=lpDD->CreateSurface(&DDsd, &lpddsOverlay, NULL);
+ }
+ if(FAILED(v)) {
+ // this video card sucks then :)
+ lpddsOverlay=NULL;
+ initing=false;
+ return 0;
+ }
+
+ // get the depth
+ m_depth=8;
+ INIT_DIRECTDRAW_STRUCT(m_ddpf);
+ if(lpddsOverlay->GetPixelFormat(&m_ddpf)>=0) {
+ m_depth=m_ddpf.dwRGBBitCount;
+ if (m_depth==16 && m_ddpf.dwGBitMask==0x03e0) m_depth=15;
+ }
+
+ lpDD->CreateClipper(0,&lpddsClipper,NULL);
+ lpddsClipper->SetHWnd(0,hwnd);
+ lpddsPrimary->SetClipper(lpddsClipper);
+ initing=false;
+ return 1;
+}
+
+int DDrawVideoOutput::onPaint(HWND hwnd, HDC hdc) {
+ return 0;
+}
+
+void DDrawVideoOutput::displayFrame(const char *buf, int size, int time) {
+ DDSURFACEDESC dd={sizeof(dd),};
+ if (m_parent->vid_vsync) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0);
+ HRESULT result;
+ if ((result=lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL)) != DD_OK) {
+ needchange=1;
+ return;
+ }
+ if(type==NSV_MAKETYPE('Y','V','1','2')) {
+ const YV12_PLANES *planes=(YV12_PLANES *)buf;
+ // convert yv12 to rgb
+ int bytes = m_depth >> 3;
+ if(m_depth==15) bytes=2;
+ int i, j, y00, y01, y10, y11, u, v;
+ unsigned char *pY = (unsigned char *)planes->y.baseAddr;
+ unsigned char *pU = (unsigned char *)planes->u.baseAddr;
+ unsigned char *pV = (unsigned char *)planes->v.baseAddr;
+ unsigned char *pOut = (unsigned char*)dd.lpSurface;
+ const int rvScale = 91881;
+ const int guScale = -22553;
+ const int gvScale = -46801;
+ const int buScale = 116129;
+ const int yScale = 65536;
+ int addOut=dd.lPitch*2-width*bytes;
+ int yrb=planes->y.rowBytes;
+ int addL=dd.lPitch;
+
+ /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
+ #define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
+
+ if(flip) {
+ pOut+=(dd.lPitch)*(height-1);
+ addOut=-dd.lPitch*2 - width*bytes;
+ addL=-addL;
+ }
+
+ for (j = 0; j <= height - 2; j += 2) {
+ for (i = 0; i <= width - 2; i += 2) {
+ y00 = *pY;
+ y01 = *(pY + 1);
+ y10 = *(pY + yrb);
+ y11 = *(pY + yrb + 1);
+ u = (*pU++) - 128;
+ v = (*pV++) - 128;
+
+ {
+ int r, g, b;
+
+ g = guScale * v + gvScale * u;
+ r = buScale * v;
+ b = rvScale * u;
+
+ y00 *= yScale; y01 *= yScale;
+ y10 *= yScale; y11 *= yScale;
+
+ switch(m_depth) {
+ case 15:
+ {
+ unsigned short *rgb=(unsigned short *)pOut;
+ rgb[0]=((LIMIT(r+y00)>>3)<<10)|((LIMIT(g+y00)>>3)<<5)|(LIMIT(b+y00)>>3);
+ rgb[1]=((LIMIT(r+y01)>>3)<<10)|((LIMIT(g+y01)>>3)<<5)|(LIMIT(b+y01)>>3);
+ rgb+=addL/2;
+ rgb[0]=((LIMIT(r+y10)>>3)<<10)|((LIMIT(g+y10)>>3)<<5)|(LIMIT(b+y10)>>3);
+ rgb[1]=((LIMIT(r+y11)>>3)<<10)|((LIMIT(g+y11)>>3)<<5)|(LIMIT(b+y11)>>3);
+ }
+ break;
+ case 16:
+ {
+ unsigned short *rgb=(unsigned short *)pOut;
+ rgb[0]=((LIMIT(r+y00)>>3)<<11)|((LIMIT(g+y00)>>2)<<5)|(LIMIT(b+y00)>>3);
+ rgb[1]=((LIMIT(r+y01)>>3)<<11)|((LIMIT(g+y01)>>2)<<5)|(LIMIT(b+y01)>>3);
+ rgb+=addL/2;
+ rgb[0]=((LIMIT(r+y10)>>3)<<11)|((LIMIT(g+y10)>>2)<<5)|(LIMIT(b+y10)>>3);
+ rgb[1]=((LIMIT(r+y11)>>3)<<11)|((LIMIT(g+y11)>>2)<<5)|(LIMIT(b+y11)>>3);
+ }
+ break;
+ case 24:
+ {
+ unsigned char *rgb=pOut;
+ /* Write out top two pixels */
+ rgb[0] = LIMIT(b+y00); rgb[1] = LIMIT(g+y00); rgb[2] = LIMIT(r+y00);
+ rgb[3] = LIMIT(b+y01); rgb[4] = LIMIT(g+y01); rgb[5] = LIMIT(r+y01);
+
+ /* Skip down to next line to write out bottom two pixels */
+ rgb += addL;
+ rgb[0] = LIMIT(b+y10); rgb[1] = LIMIT(g+y10); rgb[2] = LIMIT(r+y10);
+ rgb[3] = LIMIT(b+y11); rgb[4] = LIMIT(g+y11); rgb[5] = LIMIT(r+y11);
+ }
+ break;
+ case 32:
+ {
+ unsigned char *rgb=pOut;
+ /* Write out top two pixels */
+ rgb[0] = LIMIT(b+y00); rgb[1] = LIMIT(g+y00); rgb[2] = LIMIT(r+y00);
+ rgb[4] = LIMIT(b+y01); rgb[5] = LIMIT(g+y01); rgb[6] = LIMIT(r+y01);
+
+ /* Skip down to next line to write out bottom two pixels */
+ rgb += addL;
+ rgb[0] = LIMIT(b+y10); rgb[1] = LIMIT(g+y10); rgb[2] = LIMIT(r+y10);
+ rgb[4] = LIMIT(b+y11); rgb[5] = LIMIT(g+y11); rgb[6] = LIMIT(r+y11);
+ }
+ break;
+ }
+ }
+
+ pY += 2;
+ pOut += 2 * bytes;
+ }
+ pY += yrb+yrb-width;
+ pU += planes->u.rowBytes-width/2;
+ pV += planes->v.rowBytes-width/2;
+ pOut += addOut;
+ }
+ } else if(type==NSV_MAKETYPE('R','G','3','2')) {
+ //FUCKO: do we need to support 8bits depth?
+ switch(m_depth) {
+ case 15:
+ { // convert RGB32 -> RGB16 (555)
+ const char *a=buf;
+ char *b=(char *)dd.lpSurface;
+ int l=width*4,l2=dd.lPitch;
+ int ladj=l;
+ if (flip) { a+=l*(height-1); ladj=-ladj; }
+ for(int i=0;i<height;i++) {
+ short *dest=(short *)b;
+ int *src=(int *)a;
+ for(int j=0;j<width;j++) {
+ int c=*(src++);
+ int r=c>>16;
+ int g=(c>>8) & 0xff;
+ int b=(c) & 0xff;
+ *(dest++)=((r>>3)<<10)|((g>>3)<<5)|(b>>3);
+ }
+ a+=ladj; b+=l2;
+ }
+ }
+ break;
+ case 16:
+ { // convert RGB32 -> RGB16
+ //FUCKO: this assumes 565
+ const char *a=buf;
+ char *b=(char *)dd.lpSurface;
+ int l=width*4,l2=dd.lPitch;
+ int ladj=l;
+ if (flip) { a+=l*(height-1); ladj=-ladj; }
+ for(int i=0;i<height;i++) {
+ short *dest=(short *)b;
+ int *src=(int *)a;
+ for(int j=0;j<width;j++) {
+ //FUCKO: optimize here
+ int c=*(src++);
+ int r=c>>16;
+ int g=(c>>8) & 0xff;
+ int b=(c) & 0xff;
+ *(dest++)=((r>>3)<<11)|((g>>2)<<5)|(b>>3);
+ }
+ a+=ladj; b+=l2;
+ }
+ }
+ break;
+ case 24:
+ { // convert RGB32 -> RGB24
+ const char *a=buf;
+ char *b=(char *)dd.lpSurface;
+ int l=width*4,l2=dd.lPitch;
+ int ladj=l;
+ if (flip) { a+=l*(height-1); ladj=-ladj; }
+ for(int i=0;i<height;i++) {
+ char *dest=(char *)b;
+ int *src=(int *)a;
+ for(int j=0;j<width;j++) {
+ //FUCKO: optimize here
+ int c=*(src++);
+ int r=c>>16;
+ int g=(c>>8) & 0xff;
+ int b=(c) & 0xff;
+ *dest++=b;
+ *dest++=g;
+ *dest++=r;
+ }
+ a+=ladj; b+=l2;
+ }
+ }
+ break;
+ case 32:
+ { // straight RGB32 copy
+ const char *a=buf;
+ char *b=(char *)dd.lpSurface;
+ int l=width*4,l2=dd.lPitch;
+ int ladj=l;
+ if (flip) { a+=l*(height-1); ladj=-ladj; }
+ for(int i=0;i<height;i++) {
+ memcpy(b,a,l);
+ a+=ladj; b+=l2;
+ }
+ }
+ break;
+ }
+ } else if(type==NSV_MAKETYPE('Y','U','Y','2') || type==NSV_MAKETYPE('U','Y','V','Y')) {
+ const char *a=buf;
+ char *b=(char *)dd.lpSurface;
+ int l=width*2,l2=dd.lPitch;
+ if(flip) {
+ b+=(height-1)*l2;
+ l2=-l2;
+ }
+ switch(m_depth) {
+ case 15:
+ {
+ // yuy2->rgb16 (555) conversion
+ unsigned char *src=(unsigned char *)buf;
+ unsigned short *dst=(unsigned short *)dd.lpSurface;
+ int line, col;//, linewidth;
+ int y, yy;
+ int u, v;
+ int vr, ug, vg, ub;
+ unsigned char *py, *pu, *pv;
+
+ //linewidth = width - (width >> 1);
+ py = src;
+ pu = src + 1;
+ pv = src + 3;
+
+ int pitchadd=dd.lPitch/2-width;
+
+ for (line = 0; line < height; line++) {
+ for (col = 0; col < width; col++) {
+ #undef LIMIT
+ #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
+
+ y = *py;
+ yy = y << 8;
+ u = *pu - 128;
+ ug = 88 * u;
+ ub = 454 * u;
+ v = *pv - 128;
+ vg = 183 * v;
+ vr = 359 * v;
+
+ unsigned char b=LIMIT(yy + ub );
+ unsigned char g=LIMIT(yy - ug - vg);
+ unsigned char r=LIMIT(yy + vr);
+ *(dst++)=((r>>3)<<10)|((g>>3)<<5)|(b>>3);
+
+ py += 2;
+ if ( (col & 1) == 1) {
+ pu += 4; // skip yvy every second y
+ pv += 4; // skip yuy every second y
+ }
+ } // ..for col
+ dst+=pitchadd;
+ } /* ..for line */
+ }
+ break;
+ case 16:
+ {
+ // yuy2->rgb16 conversion
+ //FUCKO: only supports 565
+ unsigned char *src=(unsigned char *)buf;
+ unsigned short *dst=(unsigned short *)dd.lpSurface;
+ int line, col;//, linewidth;
+ int y, yy;
+ int u, v;
+ int vr, ug, vg, ub;
+ unsigned char *py, *pu, *pv;
+
+ //linewidth = width - (width >> 1);
+ py = src;
+ pu = src + 1;
+ pv = src + 3;
+
+ int pitchadd=dd.lPitch/2-width;
+
+ for (line = 0; line < height; line++) {
+ for (col = 0; col < width; col++) {
+ #undef LIMIT
+ #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
+
+ y = *py;
+ yy = y << 8;
+ u = *pu - 128;
+ ug = 88 * u;
+ ub = 454 * u;
+ v = *pv - 128;
+ vg = 183 * v;
+ vr = 359 * v;
+
+ unsigned char b=LIMIT(yy + ub );
+ unsigned char g=LIMIT(yy - ug - vg);
+ unsigned char r=LIMIT(yy + vr);
+ *(dst++)=((r>>3)<<11)|((g>>2)<<5)|(b>>3);
+
+ py += 2;
+ if ( (col & 1) ) {
+ pu += 4; // skip yvy every second y
+ pv += 4; // skip yuy every second y
+ }
+ } // ..for col
+ dst+=pitchadd;
+ } /* ..for line */ }
+ break;
+ case 24:
+ {
+ // yuy2->rgb24 conversion
+ unsigned char *src=(unsigned char *)buf;
+ unsigned char *dst=(unsigned char *)dd.lpSurface;
+ int line, col;//, linewidth;
+ int y, yy;
+ int u, v;
+ int vr, ug, vg, ub;
+ unsigned char *py, *pu, *pv;
+
+ //linewidth = width - (width >> 1);
+ py = src;
+ pu = src + 1;
+ pv = src + 3;
+
+ int pitchadd=dd.lPitch-(width*3);
+
+ for (line = 0; line < height; line++) {
+ for (col = 0; col < width; col++) {
+ #undef LIMIT
+ #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
+
+ y = *py;
+ yy = y << 8;
+ u = *pu - 128;
+ ug = 88 * u;
+ ub = 454 * u;
+ v = *pv - 128;
+ vg = 183 * v;
+ vr = 359 * v;
+
+ *(dst++)=LIMIT(yy + ub );
+ *(dst++)=LIMIT(yy - ug - vg);
+ *(dst++)=LIMIT(yy + vr);
+
+ py += 2;
+ if ( (col & 1) == 1) {
+ pu += 4; // skip yvy every second y
+ pv += 4; // skip yuy every second y
+ }
+ } // ..for col
+ dst+=pitchadd;
+ } /* ..for line */ }
+ break;
+ case 32:
+ {
+ // yuy2->rgb32 conversion
+ unsigned char *src=(unsigned char *)buf;
+ unsigned char *dst=(unsigned char *)dd.lpSurface;
+ int line, col;//, linewidth;
+ int y, yy;
+ int u, v;
+ int vr, ug, vg, ub;
+ unsigned char *py, *pu, *pv;
+
+ //linewidth = width - (width >> 1);
+ py = src;
+ pu = src + 1;
+ pv = src + 3;
+
+ int pitchadd=dd.lPitch-(width*4);
+
+ for (line = 0; line < height; line++) {
+ for (col = 0; col < width; col++) {
+ #undef LIMIT
+ #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
+
+ y = *py;
+ yy = y << 8;
+ u = *pu - 128;
+ ug = 88 * u;
+ ub = 454 * u;
+ v = *pv - 128;
+ vg = 183 * v;
+ vr = 359 * v;
+
+ *dst++ = LIMIT(yy + ub ); // b
+ *dst++ = LIMIT(yy - ug - vg); // g
+ *dst++ = LIMIT(yy + vr); // r
+ dst++;
+
+ py += 2;
+ if ( (col & 1) == 1) {
+ pu += 4; // skip yvy every second y
+ pv += 4; // skip yuy every second y
+ }
+ } // ..for col
+ dst+=pitchadd;
+ } /* ..for line */
+ }
+ break;
+ }
+ } else if(type==NSV_MAKETYPE('R','G','2','4')) {
+ //FUCKO: only ->RGB32 conversion supported
+ switch(m_depth) {
+ case 32:
+ {
+ const char *a=buf;
+ char *b=(char *)dd.lpSurface;
+ int l=width,l2=dd.lPitch;
+ int ladj=l*3;
+ if (flip) { a+=(l*3)*(height-1); ladj=-(ladj+l*3); }
+ l2-=l*4;
+ for(int i=0;i<height;i++) {
+ //memcpy(b,a,l);
+ for(int j=0;j<l;j++) {
+ b[0]=a[0];
+ b[1]=a[1];
+ b[2]=a[2];
+ b+=4; a+=3;
+ }
+ a+=ladj; b+=l2;
+ }
+ }
+ break;
+ }
+ } else if(type==NSV_MAKETYPE('R','G','B','8') && m_palette) {
+ unsigned char *d=(unsigned char *)dd.lpSurface;
+ int pitch=dd.lPitch;
+ unsigned char *src=(unsigned char *)buf;
+ int newwidth=(width+3)&0xfffc;
+ src+=newwidth*height-1;
+ for(int j=0;j<height;j++) {
+ switch(m_depth) {
+ case 15:
+ case 16:
+ {
+ unsigned short *dest=(unsigned short *)d;
+ for(int i=0;i<newwidth;i++) {
+ unsigned char c=src[-newwidth+1+i];
+ RGBQUAD *rgb=&m_palette[c];
+ switch(m_depth) {
+ case 15: *(dest++)=((rgb->rgbRed>>3)<<10)|((rgb->rgbGreen>>3)<<5)|(rgb->rgbBlue>>3); break;
+ case 16: *(dest++)=((rgb->rgbRed>>3)<<11)|((rgb->rgbGreen>>2)<<5)|(rgb->rgbBlue>>3); break;
+ }
+ }
+ }
+ break;
+ case 24:
+ case 32:
+ {
+ unsigned char *dest=d;
+ for(int i=0;i<newwidth;i++) {
+ unsigned char c=src[-newwidth+1+i];
+ RGBQUAD *rgb=&m_palette[c];
+ *dest++=rgb->rgbBlue;
+ *dest++=rgb->rgbGreen;
+ *dest++=rgb->rgbRed;
+ if(m_depth==32) dest++;
+ }
+ }
+ break;
+ }
+ d+=pitch;
+ src-=newwidth;
+ }
+ }
+
+ lpddsOverlay->Unlock(&dd);
+
+
+ RECT r;
+ HWND hwnd=m_parent->getHwnd();
+ if (!IsWindow(hwnd)) return;
+
+ if(GetParent(hwnd)) hwnd=GetParent(hwnd);
+
+ GetClientRect(hwnd,&r);
+ RECT fullr=r;
+ m_parent->adjustAspect(r);
+ if (r.left != lastresizerect.left || r.right != lastresizerect.right || r.top != lastresizerect.top ||
+ r.bottom != lastresizerect.bottom)
+ {
+ if (r.left != 0)
+ {
+ RECT tmp={0,0,r.left,fullr.bottom};
+ InvalidateRect(hwnd,&tmp,TRUE);
+ }
+
+ if (r.right != fullr.right)
+ {
+ RECT tmp={r.right,0,fullr.right,fullr.bottom};
+ InvalidateRect(hwnd,&tmp,TRUE);
+ }
+ if (r.top != 0)
+ {
+ RECT tmp={r.left,0,r.right,r.top};
+ InvalidateRect(hwnd,&tmp,TRUE);
+ }
+ if (r.bottom != fullr.bottom)
+ {
+ RECT tmp={r.left,r.bottom,r.right,fullr.bottom};
+ InvalidateRect(hwnd,&tmp,TRUE);
+ }
+
+ lastresizerect=r;
+ }
+
+ ClientToScreen(hwnd,(LPPOINT)&r);
+ ClientToScreen(hwnd,((LPPOINT)&r) + 1);
+
+ // transform coords from windows desktop coords (where 0,0==upper-left corner of box encompassing all monitors)
+ // to the coords for the monitor we're displaying on:
+ r.left-=m_mon_x;
+ r.right-=m_mon_x;
+ r.top-=m_mon_y;
+ r.bottom-=m_mon_y;
+
+ HDC hdc = NULL;
+ HDC inhdc = NULL;
+
+ RECT srcrect;
+ RECT *pSrcRect = NULL;
+
+ if (m_parent->osdShowing() && m_parent->osdReady())
+ {
+ // squish image upward to make room for the OSD.
+ int vert_margin = ((fullr.bottom-fullr.top) - (r.bottom-r.top)) / 2;
+ int pixels_to_clip = max(0, m_parent->getOSDbarHeight() - vert_margin);
+
+ // adjust source rectangle:
+ int src_y0 = (int)(height*pixels_to_clip/(float)(r.bottom-r.top) + 0.5f);
+ int src_y1 = height - src_y0;
+ SetRect(&srcrect, 0, SHOW_STREAM_TITLE_AT_TOP ? src_y0 : 0, width, src_y1);
+ pSrcRect = &srcrect;
+
+ // adjust destination rectangle:
+ r.bottom -= pixels_to_clip;
+#if (SHOW_STREAM_TITLE_AT_TOP)
+ r.top += pixels_to_clip;
+#endif
+ }
+
+ int needst=0;
+
+
+ SubsItem *mlst=m_lastsubtitle;
+ if (mlst)
+ {
+ int curw=r.right-r.left, curh=r.bottom-r.top;
+ if (!lpddsSTTemp || sttmp_w != curw || sttmp_h != curh)
+ {
+ if (lpddsSTTemp) lpddsSTTemp->Release();
+ lpddsSTTemp=0;
+
+ HRESULT v=-1;
+ DDSURFACEDESC DDsd={sizeof(DDsd),};
+ DDSURFACEDESC ddsd;
+ INIT_DIRECTDRAW_STRUCT(ddsd);
+ ddsd.dwFlags = DDSD_CAPS;
+ ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ lpddsPrimary->GetSurfaceDesc(&ddsd);
+ DDsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; //create the surface at screen depth
+ DDsd.dwWidth=sttmp_w=curw;
+ DDsd.dwHeight=sttmp_h=curh;
+ DDsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
+ if (m_parent->vid_ddraw) v=lpDD->CreateSurface(&DDsd, &lpddsSTTemp, NULL);
+ if (!m_parent->vid_ddraw || FAILED(v)) {
+ // fall back to system memory if video mem doesn't work
+ DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
+ v=lpDD->CreateSurface(&DDsd, &lpddsSTTemp, NULL);
+ }
+ m_sub_needremeasure=1;
+ }
+ if (lpddsSTTemp) needst=1;
+ }
+
+ if (needst)
+ {
+ HDC tmpdc=NULL;
+ if (!m_parent->vid_ddraw || lpddsSTTemp->Blt(NULL,lpddsOverlay,NULL,DDBLT_WAIT,0) != DD_OK) {
+ // as a last resort, BitBlt().
+ HDC tmpdc2;
+ if (lpddsOverlay->GetDC(&tmpdc2)==DD_OK) {
+ if (lpddsSTTemp->GetDC(&tmpdc)==DD_OK) {
+ BitBlt(tmpdc,0,0,sttmp_w,sttmp_h,tmpdc2,0,0,SRCCOPY);
+ }
+ }
+ }
+
+ if (tmpdc||lpddsSTTemp->GetDC(&tmpdc)==DD_OK)
+ {
+ int m_lastsubxp=mlst->xPos;
+ int m_lastsubyp=mlst->yPos;
+
+ RECT oldwinRect=winRect;
+ GetClientRect(hwnd,&winRect);
+ if(!subFont || ((winRect.bottom-winRect.top)!=(oldwinRect.bottom-oldwinRect.top)) || m_fontsize!=mlst->fontSize) {
+ if(subFont) DeleteObject(subFont);
+ m_fontsize=mlst->fontSize;
+ subFont=CreateFont(14+m_fontsize+18*(winRect.bottom-winRect.top)/768,0,0,0,FW_SEMIBOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Arial");
+ }
+
+ HWND hwnd=m_parent->getHwnd();
+ HGDIOBJ oldobj=SelectObject(tmpdc,subFont);
+
+ int centerflags=0;
+ if (m_lastsubxp < 127) centerflags |= DT_LEFT;
+ else if (m_lastsubxp > 127) centerflags |= DT_RIGHT;
+ else centerflags |= DT_CENTER;
+
+ if (m_lastsubyp < 127) centerflags |= DT_TOP;
+ else if (m_lastsubyp > 127) centerflags |= DT_BOTTOM;
+
+ if (m_sub_needremeasure)
+ {
+ subRect=r;
+ subRect.bottom-=subRect.top;
+ subRect.right -=subRect.left;
+ subRect.top=subRect.left=0;
+
+ SIZE s;
+ GetTextExtentPoint32(tmpdc,mlst->text,strlen(mlst->text),&s);
+
+ // calcul for multiline text
+ const char *p=mlst->text;
+ int n=0;
+ while(*p!=0) if(*p++=='\n') n++;
+ if(n) s.cy*=(n+1);
+
+ if (m_lastsubxp > 127) // towards the right
+ {
+ subRect.right -= ((subRect.right-subRect.left) * (255-m_lastsubxp)) / 256;
+ }
+ else if (m_lastsubxp < 127)
+ {
+ subRect.left += ((subRect.right-subRect.left) * m_lastsubxp) / 256;
+ }
+
+ subRect.top += ((subRect.bottom-s.cy-subRect.top) * m_lastsubyp)/255;
+
+ subRect.bottom=subRect.top + s.cy;
+ }
+
+ SetBkMode(tmpdc,TRANSPARENT);
+
+ // draw outline
+ SetTextColor(tmpdc,RGB(0,0,0));
+ int y=1;
+ int x=1;
+ RECT r2={subRect.left+x,subRect.top+y,subRect.right+x,subRect.bottom+y};
+ DrawText(tmpdc,mlst->text,-1,&r2,centerflags|DT_NOCLIP|DT_NOPREFIX);
+ // draw text
+ SetTextColor(tmpdc,RGB(mlst->colorRed,mlst->colorGreen,mlst->colorBlue));
+ DrawText(tmpdc,mlst->text,-1,&subRect,centerflags|DT_NOCLIP|DT_NOPREFIX);
+ SelectObject(tmpdc,oldobj);
+ lpddsSTTemp->ReleaseDC(tmpdc);
+ }
+ if (!m_parent->vid_ddraw || lpddsPrimary->Blt(&r,lpddsSTTemp,pSrcRect,DDBLT_WAIT,0) != DD_OK) {
+ // as a last resort, BitBlt().
+ if (lpddsSTTemp->GetDC(&inhdc)==DD_OK) {
+ if (lpddsPrimary->GetDC(&hdc)==DD_OK) {
+ int src_w = width;
+ int src_h = pSrcRect ? (pSrcRect->bottom - pSrcRect->top) : height;
+ if (r.right-r.left == src_w && r.bottom-r.top == src_h)
+ BitBlt(hdc,r.left,r.top,r.right-r.left,r.bottom-r.top,inhdc,0,0,SRCCOPY);
+ else
+ StretchBlt(hdc,r.left,r.top,r.right-r.left,r.bottom-r.top,inhdc,0,0,src_w,src_h,SRCCOPY);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!m_parent->vid_ddraw || lpddsPrimary->Blt(&r,lpddsOverlay,pSrcRect,DDBLT_WAIT,0) != DD_OK) {
+ // as a last resort, BitBlt().
+ if (lpddsOverlay->GetDC(&inhdc)==DD_OK) {
+ if (lpddsPrimary->GetDC(&hdc)==DD_OK) {
+ int src_w = width;
+ int src_h = pSrcRect ? (pSrcRect->bottom - pSrcRect->top) : height;
+ if (r.right-r.left == src_w && r.bottom-r.top == src_h)
+ BitBlt(hdc,r.left,r.top,r.right-r.left,r.bottom-r.top,inhdc,0,0,SRCCOPY);
+ else
+ StretchBlt(hdc,r.left,r.top,r.right-r.left,r.bottom-r.top,inhdc,0,0,src_w,src_h,SRCCOPY);
+ }
+ }
+ }
+ }
+
+#if 0 //faster style
+ if (m_parent->osdShowing())
+ {
+ if (hdc || lpddsPrimary->GetDC(&hdc)==DD_OK)
+ m_parent->drawOSD(hdc, &r);
+ }
+#endif
+
+ if (hdc) { lpddsPrimary->ReleaseDC(hdc); hdc = NULL; }
+ if (inhdc) { lpddsOverlay->ReleaseDC(inhdc); inhdc = NULL; }
+
+#if 1 // safer style
+ if (m_parent->osdShowing())
+ {
+ HWND h=m_parent->getHwnd();
+ hdc=GetDC(h);
+ m_parent->drawOSD(hdc, &r);
+ ReleaseDC(h,hdc);
+ }
+#endif
+}
+
+void DDrawVideoOutput::goFullScreen() {
+}
+
+void DDrawVideoOutput::removeFullScreen() {
+}
+
+void DDrawVideoOutput::timerCallback() {
+}
+
+int DDrawVideoOutput::showOSD() {
+ return 1;
+}
+
+void DDrawVideoOutput::hideOSD() {
+ // repaint the client area, to black, where there is no video
+ // (otherwise the OSD might be left painted there)
+
+ RECT r;
+ HWND hwnd=m_parent->getHwnd();
+ if(GetParent(hwnd)) hwnd=GetParent(hwnd);
+ GetClientRect(hwnd,&r);
+
+ HDC hdc = GetDC(hwnd);
+ if (hdc) {
+ HGDIOBJ oldobj1=SelectObject(hdc,CreateSolidBrush(RGB(0,0,0)));
+ HGDIOBJ oldobj2=SelectObject(hdc,CreatePen(PS_SOLID,0,RGB(0,0,0)));
+ int margin = ((r.bottom - r.top) - (lastresizerect.bottom - lastresizerect.top) + 1) / 2;
+ Rectangle(hdc,r.left,r.top,r.right,r.top + margin);
+ Rectangle(hdc,r.left,r.bottom - margin,r.right,r.bottom);
+ margin = ((r.right - r.left) - (lastresizerect.right - lastresizerect.left) + 1) / 2;
+ Rectangle(hdc,r.left,r.top,r.left + margin,r.bottom);
+ Rectangle(hdc,r.right - margin,r.top,r.right,r.bottom);
+ DeleteObject(SelectObject(hdc,oldobj2));
+ DeleteObject(SelectObject(hdc,oldobj1));
+
+ ReleaseDC(hwnd, hdc);
+ }
+}
+
+void DDrawVideoOutput::resetSubtitle()
+{
+ m_lastsubtitle=0;
+} \ No newline at end of file
diff --git a/Src/nsv/nsvplay/vid_ddraw.h b/Src/nsv/nsvplay/vid_ddraw.h
new file mode 100644
index 00000000..0feea9f5
--- /dev/null
+++ b/Src/nsv/nsvplay/vid_ddraw.h
@@ -0,0 +1,63 @@
+#ifndef _VIDEO_DDRAW_H
+#define _VIDEO_DDRAW_H
+
+#include <ddraw.h>
+#include "video.h"
+
+class SubsItem;
+
+class DDrawVideoOutput : public VideoOutputChild {
+public:
+ DDrawVideoOutput();
+ virtual ~DDrawVideoOutput();
+
+ int create(VideoOutput *parent, int w, int h, unsigned int type, int flipit, double aspectratio); //return 1 if ok
+ int needChange() { return needchange; }
+
+ int onPaint(HWND hwnd, HDC hdc);
+ void displayFrame(const char *buf, int size, int time);
+
+ void goFullScreen();
+ void removeFullScreen();
+
+ void timerCallback();
+
+ void setPalette(RGBQUAD *pal) { m_palette=pal; }
+
+ int showOSD();
+ void hideOSD();
+
+ void drawSubtitle(SubsItem *item);
+ void resetSubtitle();
+
+private:
+ int width, height, flip;
+ int needchange;
+ unsigned int type;
+ VideoOutput *m_parent;
+ LPDIRECTDRAW lpDD;
+ LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary, lpddsSTTemp;
+ int sttmp_w, sttmp_h;
+ DDCAPS capsDrv;
+ unsigned int uDestSizeAlign, uSrcSizeAlign;
+ DWORD dwUpdateFlags;
+ RECT rs,rd;
+ RECT lastresizerect;
+
+ bool initing;
+ int is_fullscreen;
+
+ LPDIRECTDRAWCLIPPER lpddsClipper;
+ DDPIXELFORMAT m_ddpf;
+ int m_depth;
+ RGBQUAD *m_palette;
+
+ HFONT subFont;
+ RECT subRect;
+ SubsItem *m_lastsubtitle;
+ int m_sub_needremeasure;
+ RECT winRect;
+ int m_fontsize;
+};
+
+#endif
diff --git a/Src/nsv/nsvplay/vid_overlay.cpp b/Src/nsv/nsvplay/vid_overlay.cpp
new file mode 100644
index 00000000..efe32661
--- /dev/null
+++ b/Src/nsv/nsvplay/vid_overlay.cpp
@@ -0,0 +1,654 @@
+#include "video.h"
+#include <multimon.h>
+#include "subtitles.h"
+
+#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
+#define OV_COL_R 16
+#define OV_COL_G 0
+#define OV_COL_B 16
+
+OverlayVideoOutput::OverlayVideoOutput() {
+ lpDD=NULL;
+ lpddsOverlay=NULL;
+ lpddsPrimary=NULL;
+ is_fullscreen=0;
+ yuy2_output=uyvy_output=0;
+ m_parent=NULL;
+ initing=false;
+ needchange=0;
+ memset(&m_oldrd,0,sizeof(m_oldrd));
+ memset(&winRect,0,sizeof(winRect));
+ subFont=NULL;
+ m_fontsize=0;
+ resetSubtitle();
+}
+
+OverlayVideoOutput::~OverlayVideoOutput() {
+ if(is_fullscreen) removeFullScreen();
+ LPDIRECTDRAWSURFACE o=lpddsOverlay;
+ lpddsOverlay=NULL;
+ if(o) o->Release();
+ if(lpddsPrimary) lpddsPrimary->Release();
+ if (lpDD) lpDD->Release(); // BU added NULL check in response to talkback
+ if(subFont) DeleteObject(subFont);
+}
+
+static DWORD DD_ColorMatch(LPDIRECTDRAWSURFACE pdds, COLORREF rgb)
+{
+ COLORREF rgbT;
+ HDC hdc;
+ DWORD dw = CLR_INVALID;
+ DDSURFACEDESC ddsd;
+ HRESULT hres;
+
+ //
+ // use GDI SetPixel to color match for us
+ //
+ if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
+ {
+ rgbT = GetPixel(hdc, 0, 0); // save current pixel value
+ SetPixel(hdc, 0, 0, rgb); // set our value
+ pdds->ReleaseDC(hdc);
+ }
+
+ //
+ // now lock the surface so we can read back the converted color
+ //
+ ddsd.dwSize = sizeof(ddsd);
+ while ((hres = pdds->Lock(NULL, &ddsd, 0, NULL)) ==
+ DDERR_WASSTILLDRAWING)
+ ;
+
+ if (hres == DD_OK)
+ {
+ dw = *(DWORD *)ddsd.lpSurface; // get DWORD
+ if(ddsd.ddpfPixelFormat.dwRGBBitCount<32)
+ dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1; // mask it to bpp
+ pdds->Unlock(NULL);
+ }
+
+ //
+ // now put the color that was there back.
+ //
+ if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
+ {
+ SetPixel(hdc, 0, 0, rgbT);
+ pdds->ReleaseDC(hdc);
+ }
+
+ return dw;
+}
+
+int OverlayVideoOutput::create(VideoOutput *parent, int w, int h, unsigned int ptype, int flipit, double aspectratio) {
+ type=ptype;
+ width=w;
+ height=h;
+ flip=flipit;
+ m_parent=parent;
+
+ initing=true;
+ HWND hwnd=parent->getHwnd();
+
+ if (lpDD) lpDD->Release();
+ lpDD=NULL;
+
+ update_monitor_coords(parent);
+
+ if(!m_found_devguid) DirectDrawCreate(NULL,&lpDD,NULL);
+ else DirectDrawCreate(&m_devguid,&lpDD,NULL);
+
+ if(!lpDD) {
+ initing=false;
+ return 0;
+ }
+
+ lpDD->SetCooperativeLevel(hwnd,DDSCL_NOWINDOWCHANGES|DDSCL_NORMAL);
+
+ DDSURFACEDESC ddsd;
+ INIT_DIRECTDRAW_STRUCT(ddsd);
+ ddsd.dwFlags = DDSD_CAPS;
+ ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ HRESULT ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL );
+
+ // init overlay
+ DDSURFACEDESC ddsdOverlay;
+ INIT_DIRECTDRAW_STRUCT(ddsdOverlay);
+ ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
+ ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT|DDSD_PITCH;
+ ddsdOverlay.dwWidth=w;
+ ddsdOverlay.dwHeight=h;
+ ddsdOverlay.lPitch=w*4;
+ ddsdOverlay.dwBackBufferCount=0;
+ DDPIXELFORMAT pf[]=
+ {
+ {sizeof(DDPIXELFORMAT),DDPF_FOURCC,MAKEFOURCC('Y','U','Y','2'),0,0,0,0,0},
+ {sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('U','Y','V','Y'),0,0,0,0,0}, // UYVY
+ {sizeof(DDPIXELFORMAT),DDPF_FOURCC,MAKEFOURCC('Y','V','1','2'),0,0,0,0,0},
+ };
+ int tab[5];
+ if(type==NSV_MAKETYPE('Y','U','Y','2')) {
+ tab[0]=0; // default is YUY2
+ tab[1]=1;
+ tab[2]=-1;
+ } else if(type==NSV_MAKETYPE('U','Y','V','Y')) {
+ tab[0]=1; // make UYVY default
+ tab[1]=0;
+ tab[2]=-1;
+ } else if(type==NSV_MAKETYPE('Y','V','1','2')) {
+ /*tab[0]=2;
+ tab[1]=0;
+ tab[2]=1;
+ tab[3]=-1;*/
+ //CT> Make YUY2 default too, cause YV12 is borked on some ATI cards/drivers :(
+ tab[0]=0;
+ tab[1]=1;
+ tab[2]=-1;
+ } else {
+ tab[0]=-1; // default is RGB
+ }
+
+ int x=4096;
+ HRESULT v=-1;
+ for (x = 0; x < sizeof(tab)/sizeof(tab[0]) && tab[x]>=0; x ++) {
+ ddsdOverlay.ddpfPixelFormat=pf[tab[x]];
+ v=lpDD->CreateSurface(&ddsdOverlay, &lpddsOverlay, NULL);
+ if (!FAILED(v)) break;
+ }
+ if(FAILED(v)||x>=sizeof(tab)/sizeof(tab[0])||tab[x]<0) {
+ initing=false;
+ return 0;
+ }
+
+ yuy2_output = (tab[x] == 0);
+ uyvy_output = (tab[x] == 1);
+
+ INIT_DIRECTDRAW_STRUCT(capsDrv);
+ ddrval = lpDD->GetCaps(&capsDrv, NULL);
+
+ uDestSizeAlign = capsDrv.dwAlignSizeDest;
+ uSrcSizeAlign = capsDrv.dwAlignSizeSrc;
+
+ dwUpdateFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE;
+
+ DEVMODE d;
+ d.dmSize=sizeof(d);
+ d.dmDriverExtra=0;
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &d);
+
+ int rv=OV_COL_R, gv=OV_COL_G, bv=OV_COL_B;
+
+ INIT_DIRECTDRAW_STRUCT(ovfx);
+ ovfx.dwDDFX=0;
+ switch(d.dmBitsPerPel) {
+ case 16:
+ ovfx.dckDestColorkey.dwColorSpaceLowValue=((rv>>3) << 11) | ((gv>>2) << 5) | (bv>>3);
+ break;
+ case 15:
+ ovfx.dckDestColorkey.dwColorSpaceLowValue=((rv>>3) << 10) | ((gv>>3) << 5) | (bv>>3);
+ break;
+ case 24: case 32:
+ ovfx.dckDestColorkey.dwColorSpaceLowValue=(rv << 16) | (gv << 8) | bv;
+ break;
+ }
+
+ //try to get the correct bit depth thru directdraw (for fucked up 16 bits displays for ie.)
+ {
+ DDSURFACEDESC DDsd={sizeof(DDsd),};
+ lpddsPrimary->GetSurfaceDesc(&ddsd);
+ DDsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; //create the surface at screen depth
+ DDsd.dwWidth=8;
+ DDsd.dwHeight=8;
+ DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
+ LPDIRECTDRAWSURFACE tempsurf;
+ if(lpDD->CreateSurface(&DDsd, &tempsurf, NULL)==DD_OK)
+ {
+ int res=DD_ColorMatch(tempsurf, RGB(rv,gv,bv));
+ if(res!=CLR_INVALID) ovfx.dckDestColorkey.dwColorSpaceLowValue=res;
+ tempsurf->Release();
+ }
+ }
+
+ ovfx.dckDestColorkey.dwColorSpaceHighValue=ovfx.dckDestColorkey.dwColorSpaceLowValue;
+
+ getRects(&rs,&rd);
+ if(FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx))) {
+ initing=false;
+ return 0;
+ }
+ initing=false;
+
+ DDSURFACEDESC dd={sizeof(dd),};
+ if (lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL) != DD_OK) return 0;
+ unsigned char *o=(unsigned char*)dd.lpSurface;
+ if (uyvy_output||yuy2_output)
+ {
+ int x=dd.lPitch*height/2;
+ while (x--)
+ {
+ if (uyvy_output)
+ {
+ *o++=128;
+ *o++=0;
+ }
+ else
+ {
+ *o++=0;
+ *o++=-128;
+ }
+ }
+ }
+ else
+ {
+ memset(o,0,dd.lPitch*height); o+=dd.lPitch*height;
+ memset(o,128,dd.lPitch*height/2);
+ }
+ lpddsOverlay->Unlock(&dd);
+
+ InvalidateRect(hwnd,NULL,TRUE);
+ return 1;
+}
+
+void OverlayVideoOutput::getRects(RECT *drs, RECT *drd) {
+ HWND hwnd=m_parent->getHwnd();
+ if(GetParent(hwnd)) hwnd=GetParent(hwnd);
+
+ RECT rd,rs;
+ GetClientRect(hwnd,&rd);
+ ClientToScreen(hwnd,(LPPOINT)&rd);
+ ClientToScreen(hwnd,((LPPOINT)&rd) + 1);
+
+ m_parent->adjustAspect(rd);
+ rd.left-=m_mon_x;
+ rd.right-=m_mon_x;
+ rd.top-=m_mon_y;
+ rd.bottom-=m_mon_y;
+
+ memset(&rs,0,sizeof(rs));
+ rs.right=width;
+ rs.bottom=height;
+
+ //resize overlay for off-screen
+ RECT rfull;
+ //m_parent->getViewport(&rfull,NULL,1); //FUCKO: assume monitor 0
+ m_parent->getViewport(&rfull,hwnd,1); //FUCKO: okay to use this hwnd? (fixes multimon! -RG)
+ if(rd.right>rfull.right) {
+ int diff=rd.right-rfull.right;
+ float sc=(float)(width)/(float)(rd.right-rd.left);
+ rd.right=rfull.right;
+ rs.right=width-(int)(diff*sc);
+ }
+ if(rd.left<rfull.left) {
+ int diff=rfull.left-rd.left;
+ float sc=(float)(width)/(float)(rd.right-rd.left);
+ rd.left=rfull.left;
+ rs.left=(int)(diff*sc);
+ }
+ if(rd.bottom>rfull.bottom) {
+ int diff=rd.bottom-rfull.bottom;
+ float sc=(float)(height)/(float)(rd.bottom-rd.top);
+ rd.bottom=rfull.bottom;
+ rs.bottom=height-(int)(diff*sc);
+ }
+ if(rd.top<rfull.top) {
+ int diff=rfull.top-rd.top;
+ float sc=(float)(height)/(float)(rd.bottom-rd.top);
+ rd.top=rfull.top;
+ rs.top=(int)(diff*sc);
+ }
+
+ if (capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC && uDestSizeAlign) {
+ rs.left = (int)((rs.left+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;
+ rs.right = (int)((rs.right+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;
+ }
+ if (capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST && uDestSizeAlign) {
+ rd.left = (int)((rd.left+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;
+ rd.right = (int)((rd.right+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;
+ }
+
+ *drd=rd;
+ *drs=rs;
+}
+
+void OverlayVideoOutput::timerCallback() {
+ if(!m_parent) return;
+
+ RECT rd,rs;
+ getRects(&rs,&rd);
+
+ if(memcmp(&m_oldrd,&rd,sizeof(RECT))) {
+ m_oldrd=rd;
+ if(!initing && lpddsOverlay)
+ if(FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx))) {
+ needchange=1;
+ }
+ }
+}
+
+int OverlayVideoOutput::onPaint(HWND hwnd, HDC hdc) {
+ if(!m_parent) return 0;
+
+ PAINTSTRUCT p;
+ BeginPaint(hwnd,&p);
+
+ RECT r;
+ GetClientRect(hwnd,&r);
+ LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),};
+ HBRUSH br=CreateBrushIndirect(&lb);
+ FillRect(p.hdc,&r,br);
+ DeleteObject(br);
+
+ if (curSubtitle)
+ {
+ int m_lastsubxp=curSubtitle->xPos;
+ int m_lastsubyp=curSubtitle->yPos;
+
+ HDC out=p.hdc;
+
+ HGDIOBJ oldobj=SelectObject(out,subFont);
+
+ SetBkMode(out,TRANSPARENT);
+ int centerflags=0;
+ if (m_lastsubxp < 127) centerflags |= DT_LEFT;
+ else if (m_lastsubxp > 127) centerflags |= DT_RIGHT;
+ else centerflags |= DT_CENTER;
+
+ if (m_lastsubyp < 127) centerflags |= DT_TOP;
+ else if (m_lastsubyp > 127) centerflags |= DT_BOTTOM;
+
+ // draw outline
+ SetTextColor(out,RGB(0,0,0));
+ for (int y = -1; y < 2; y++)
+ for (int x = -1; x < 2; x++)
+ {
+ if(!y && !x) continue;
+ RECT r2={subRect.left+x,subRect.top+y,subRect.right+x,subRect.bottom+y};
+ DrawText(out,curSubtitle->text,-1,&r2,centerflags|DT_NOCLIP|DT_NOPREFIX);
+ }
+ // draw text
+ SetTextColor(out,RGB(curSubtitle->colorRed,curSubtitle->colorGreen,curSubtitle->colorBlue));
+ DrawText(out,curSubtitle->text,-1,&subRect,centerflags|DT_NOCLIP|DT_NOPREFIX);
+ SelectObject(out,oldobj);
+ }
+
+ EndPaint(hwnd,&p);
+
+ return 1;
+}
+
+void OverlayVideoOutput::displayFrame(const char *buf, int size, int time) {
+ if(!m_parent) return;
+
+ DDSURFACEDESC dd={sizeof(dd),};
+ if (m_parent->vid_vsync) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0);
+ HRESULT result;
+ if ((result=lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL)) != DD_OK) {
+ //CT>FUCKO:reenable me (ctrl+alt+del on win2k)
+ //if(result==DDERR_SURFACELOST) width=-1; //will try to recreate the surface in the next processData() call
+ return;
+ }
+ if(type==NSV_MAKETYPE('Y','V','1','2')) {
+ const YV12_PLANES *planes=(YV12_PLANES *)buf;
+ if (uyvy_output||yuy2_output) { // YV12planar->UYVY or YUY2
+ unsigned char *o=(unsigned char*)dd.lpSurface;
+ const unsigned char *yi=planes->y.baseAddr;
+ const unsigned char *ui=planes->u.baseAddr;
+ const unsigned char *vi=planes->v.baseAddr;
+ int y=height;
+ if (flip) o+=dd.lPitch*(height-1);
+ while (y>0) {
+ int x=width;
+ unsigned char *oo=o;
+
+ if (uyvy_output) while (x>0) {
+ o[0]=*ui++; o[1]=*yi++; o[2]=*vi++; o[3]=*yi++;
+ o+=4; x-=2;
+ }
+ else while (x>0) {
+ o[0]=*yi++; o[1]=*ui++; o[2]=*yi++; o[3]=*vi++;
+ o+=4; x-=2;
+ }
+ ui-=width/2;
+ vi-=width/2;
+ yi+=planes->y.rowBytes-width;
+ x=width;
+ if (flip) o=oo-dd.lPitch;
+ else o+=dd.lPitch-width*2;
+ oo=o;
+ if (uyvy_output) while (x>0) {
+ o[0]=*ui++; o[1]=*yi++; o[2]=*vi++; o[3]=*yi++;
+ o+=4; x-=2;
+ } else while (x>0) {
+ o[0]=*yi++; o[1]=*ui++; o[2]=*yi++; o[3]=*vi++;
+ o+=4; x-=2;
+ }
+ if (flip) o=oo-dd.lPitch;
+ else o+=dd.lPitch-width*2;
+ ui+=planes->u.rowBytes-(width/2);
+ vi+=planes->v.rowBytes-(width/2);
+ yi+=planes->y.rowBytes-width;
+ y-=2;
+ }
+ } else { // woo native YV12 copy
+ int f=!!flip;
+ char *o=(char*)dd.lpSurface+(f*height*dd.lPitch);
+ const char *i=(const char*)planes->y.baseAddr;
+ int d_o=dd.lPitch;
+ if (f) d_o=-d_o;
+ else o-=d_o;
+
+ int h2=height;
+ while (h2--) {
+ o+=d_o; memcpy(o,i,width); i+=planes->y.rowBytes;
+ }
+
+ d_o/=2;
+
+ int w2=width/2;
+ h2=height/2;
+ i=(const char*)planes->v.baseAddr;
+ o=(char*)dd.lpSurface+(height*dd.lPitch*(f+4))/4;
+
+ if (!f) o-=d_o;
+ while (h2--) {
+ o+=d_o; memcpy(o,i,w2); i+=planes->v.rowBytes;
+ }
+ o=(char*)dd.lpSurface+(height*dd.lPitch*(f+5))/4;
+ i=(const char*)planes->u.baseAddr;
+ h2=height/2;
+
+ if (!f) o-=d_o;
+ while (h2--) {
+ o+=d_o; memcpy(o,i,w2);i+=planes->u.rowBytes;
+ }
+ }
+ } else if(type==NSV_MAKETYPE('Y','U','Y','2') || type==NSV_MAKETYPE('U','Y','V','Y')) {
+ const char *a=buf;
+ char *b=(char *)dd.lpSurface;
+ int l=width*2,l2=dd.lPitch;
+ if(flip) {
+ b+=(height-1)*l2;
+ l2=-l2;
+ }
+ int is_uyvy=type==NSV_MAKETYPE('U','Y','V','Y');
+ if (uyvy_output && !is_uyvy || (yuy2_output && is_uyvy)) // convert to uyvy
+ {
+ for(int i=0;i<height;i++) {
+ int x=width/2;
+ while (x-->0) {
+ b[0]=a[1];
+ b[1]=a[0];
+ b[2]=a[3];
+ b[3]=a[2];
+ a+=4;
+ b+=4;
+ }
+ memcpy(b,a,l);
+ b+=l2;
+ a+=l;
+ }
+ } else {
+ //wee straight YUY2 copy
+ for(int i=0;i<height;i++) {
+ memcpy(b,a,l);
+ b+=l2;
+ a+=l;
+ }
+ }
+ }
+
+ lpddsOverlay->Unlock(&dd);
+
+ if (m_parent->osdShowing())
+ {
+ RECT rs, rd;
+ getRects(&rs,&rd);
+
+ HDC hdc;
+#if 1 // set both these 1s to 0s to put it back on ryan's mode
+ HWND h=m_parent->getHwnd();
+ hdc=GetDC(h);
+#else
+ if (lpddsPrimary->GetDC(&hdc)==DD_OK)
+ {
+#endif
+ m_parent->drawOSD(hdc, &rd);
+#if 1
+ ReleaseDC(h,hdc);
+#else
+ lpddsPrimary->ReleaseDC(hdc);
+ }
+#endif
+ }
+}
+
+void OverlayVideoOutput::goFullScreen() {
+/* fullscreen_controls = new GuiObjectWnd;
+ fullscreen_controls->setContent("video.fullscreen_controls");
+ fullscreen_controls->init(m_parent);
+
+ RECT r;
+ Std::getViewport(&r,m_parent->gethWnd(),1);
+
+ RECT nr = r;
+ nr.top = (int)(r.bottom - (r.bottom - r.top) * 0.15);
+ nr.bottom = (int)(r.bottom - (r.bottom - r.top) * 0.05);
+ fullscreen_controls->resizeToRect(&nr);
+ */
+ is_fullscreen=1;
+}
+
+void OverlayVideoOutput::removeFullScreen() {
+/* delete fullscreen_controls;
+ fullscreen_controls = NULL;*/
+ is_fullscreen=0;
+}
+
+int OverlayVideoOutput::showOSD() {
+// if (fullscreen_controls != NULL) fullscreen_controls->setVisible(TRUE);
+
+ // enabling the following code will cause the top & bottom OSD bars
+ // to squish the image (instead of crop it):
+ /*if(lpddsOverlay) {
+ RECT rd,rs;
+ getRects(&rs,&rd);
+
+ HWND hwnd=m_parent->getHwnd();
+ if(GetParent(hwnd)) hwnd=GetParent(hwnd);
+
+ RECT temp;
+ GetClientRect(hwnd,&temp);
+ int bottom_margin = ((temp.bottom-temp.top) - (rd.bottom-rd.top)) / 2;
+ int pixels_to_clip = max(0, m_parent->getOSDbarHeight() - bottom_margin);
+ rd.bottom -= pixels_to_clip;
+
+ lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx);
+ }*/
+
+
+ return 1;
+}
+
+void OverlayVideoOutput::hideOSD() {
+ //if (fullscreen_controls != NULL) fullscreen_controls->setVisible(FALSE);
+
+ // 1) repaint the OSD area with the overlay color here
+ HWND hwnd = m_parent->getHwnd();
+ if(GetParent(hwnd)) hwnd=GetParent(hwnd);
+
+ HDC hdc = GetDC(hwnd);
+ if (hdc) {
+ RECT r;
+ GetClientRect(hwnd,&r);
+ LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),};
+ HBRUSH br=CreateBrushIndirect(&lb);
+ FillRect(hdc,&r,br);
+ DeleteObject(br);
+
+ ReleaseDC(hwnd, hdc);
+ }
+
+ // 2) readjust the overlay destination rectangle
+ /*if(lpddsOverlay) {
+ RECT rd,rs;
+ getRects(&rs,&rd);
+ lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx);
+ }*/
+
+}
+
+void OverlayVideoOutput::drawSubtitle(SubsItem *item) {
+ curSubtitle=item;
+
+ HWND hwnd=m_parent->getHwnd();
+
+ RECT oldrect=subRect;
+ GetClientRect(hwnd,&subRect);
+
+ if(item) {
+
+ RECT oldwinRect=winRect;
+ GetClientRect(hwnd,&winRect);
+ if(!subFont || ((winRect.bottom-winRect.top)!=(oldwinRect.bottom-oldwinRect.top)) || m_fontsize!=item->fontSize) {
+ if(subFont) DeleteObject(subFont);
+ m_fontsize=item->fontSize;
+ subFont=CreateFont(14+item->fontSize+18*(winRect.bottom-winRect.top)/768,0,0,0,FW_SEMIBOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Arial");
+ }
+
+ HDC out=GetDC(hwnd);
+ SelectObject(out,subFont);
+ SIZE s;
+ GetTextExtentPoint32(out,item->text,strlen(item->text),&s);
+ {
+ // calcul for multiline text
+ const char *p=item->text;
+ int n=0;
+ while(*p!=0) if(*p++=='\n') n++;
+ if(n) s.cy*=(n+1);
+ }
+
+ if (item->xPos > 127) // towards the right
+ {
+ subRect.right -= ((subRect.right-subRect.left) * (255-item->xPos)) / 256;
+ }
+ else if (item->xPos < 127)
+ {
+ subRect.left += ((subRect.right-subRect.left) * item->xPos) / 256;
+ }
+
+ subRect.top += ((subRect.bottom-s.cy-subRect.top) * item->yPos)/255;
+
+ subRect.bottom=subRect.top + s.cy;
+
+ ReleaseDC(hwnd,out);
+ }
+
+ //just redraw the correct portion
+ InvalidateRect(hwnd,&oldrect,TRUE);
+ InvalidateRect(hwnd,&subRect,TRUE);
+}
+
+void OverlayVideoOutput::resetSubtitle()
+{
+ curSubtitle=NULL;
+ subRect.top=65536;
+} \ No newline at end of file
diff --git a/Src/nsv/nsvplay/vid_overlay.h b/Src/nsv/nsvplay/vid_overlay.h
new file mode 100644
index 00000000..f4772e69
--- /dev/null
+++ b/Src/nsv/nsvplay/vid_overlay.h
@@ -0,0 +1,58 @@
+#ifndef _VIDEO_OVERLAY_H
+#define _VIDEO_OVERLAY_H
+
+#include <ddraw.h>
+#include <multimon.h>
+#include "video.h"
+
+class SubsItem;
+
+class OverlayVideoOutput : public VideoOutputChild {
+public:
+ OverlayVideoOutput();
+ virtual ~OverlayVideoOutput();
+
+ int create(VideoOutput *parent, int w, int h, unsigned int type, int flipit, double aspectratio); //return 1 if ok
+ int needChange() { return needchange; }
+
+ int onPaint(HWND hwnd, HDC hdc);
+ void displayFrame(const char *buf, int size, int time);
+
+ void goFullScreen();
+ void removeFullScreen();
+
+ void timerCallback();
+
+ int showOSD();
+ void hideOSD();
+
+ void drawSubtitle(SubsItem *item);
+ virtual void resetSubtitle();
+
+private:
+ int width, height, flip;
+ int needchange;
+ unsigned int type;
+ VideoOutput *m_parent;
+ LPDIRECTDRAW lpDD;
+ LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary;
+ DDCAPS capsDrv;
+ unsigned int uDestSizeAlign, uSrcSizeAlign;
+ DWORD dwUpdateFlags;
+ DDOVERLAYFX ovfx;
+ RECT rs,rd;
+ RECT m_oldrd;
+ RECT winRect;
+
+ bool initing;
+ int is_fullscreen, yuy2_output, uyvy_output;
+
+ void getRects(RECT *drs, RECT *drd);
+
+ HFONT subFont;
+ RECT subRect;
+ SubsItem *curSubtitle;
+ int m_fontsize;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nsv/nsvplay/video.cpp b/Src/nsv/nsvplay/video.cpp
new file mode 100644
index 00000000..8a6532c7
--- /dev/null
+++ b/Src/nsv/nsvplay/video.cpp
@@ -0,0 +1,964 @@
+#include <windows.h>
+#include <ddraw.h>
+#include "main.h"
+#include "video.h"
+#include "subtitles.h"
+
+#include "resource.h"
+
+#undef GetSystemMetrics
+
+#define OSD_ENABLED 1
+
+#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
+#define OV_COL_R 16
+#define OV_COL_G 0
+#define OV_COL_B 16
+#define OSD_TEXT_SIZE 28
+#define OSD_TEXT_R 192
+#define OSD_TEXT_G 192
+#define OSD_TEXT_B 192
+#define OSD_TEXT_R_HILITE 255
+#define OSD_TEXT_G_HILITE 255
+#define OSD_TEXT_B_HILITE 255
+#define OSD_VOL_COL_R 0
+#define OSD_VOL_COL_G 0
+#define OSD_VOL_COL_B 192
+#define OSD_VOL_BKCOL_R 0
+#define OSD_VOL_BKCOL_G 0
+#define OSD_VOL_BKCOL_B 64
+
+#define TIMER_OSD_ID 1234
+
+#define CTRLTYPE_SYMBOL 0
+#define CTRLTYPE_TEXT 1
+#define CTRLTYPE_PROGRESS 2
+#define CTRLTYPE_SPACER 3
+
+#define CTRL_PROGRESSTEXT 0
+#define CTRL_PROGRESS 1
+#define CTRL_PROGRESSSPACER 2
+#define CTRL_REW 3
+#define CTRL_PLAY 4
+#define CTRL_PAUSE 5
+#define CTRL_STOP 6
+#define CTRL_FFWD 7
+#define CTRL_VOLSPACER 8
+#define CTRL_VOLTEXT 9
+#define CTRL_VOL 10
+
+int g_ctrl_type[NUM_WIDGETS] = {
+ CTRLTYPE_TEXT,
+ CTRLTYPE_PROGRESS,
+ CTRLTYPE_SPACER,
+ CTRLTYPE_SYMBOL,
+ CTRLTYPE_SYMBOL,
+ CTRLTYPE_SYMBOL,
+ CTRLTYPE_SYMBOL,
+ CTRLTYPE_SYMBOL,
+ CTRLTYPE_SPACER,
+ CTRLTYPE_TEXT,
+ CTRLTYPE_PROGRESS
+};
+
+const char *g_ctrl_text[NUM_WIDGETS] = {
+ "Progress ",
+ "",
+ "",
+ "7", // rew
+ "4", // play
+ ";", // pause
+ "<", // stop
+ "8", // ffwd
+ "",
+ "Volume ",
+ ""
+};
+
+int g_ctrl_force_width[NUM_WIDGETS] = {
+ 0,
+ 96, // progress bar width
+ 32, // spacer width
+ 0, // rew
+ 0, // play
+ 0, // pause
+ 0, // stop
+ 0, // ffwd
+ 32, // spacer width
+ 0,
+ 64 // volume bar width
+};
+
+extern HINSTANCE g_hInstance;
+extern int g_bitmap_id;
+
+static BOOL WINAPI DDEnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm) {
+ VideoOutputChild *ovo=(VideoOutputChild *)lpContext;
+ if(ovo->m_found_devguid) return 1;
+ if(hm==ovo->m_monitor_to_find) {
+ ovo->m_devguid=*lpGUID;
+ ovo->m_found_devguid=1;
+ }
+ return 1;
+}
+
+void VideoOutputChild::update_monitor_coords(VideoOutput *parent)
+{
+ //find the correct monitor if multiple monitor support is present
+ HWND hwnd=parent->getHwnd();
+ m_found_devguid=0;
+ m_mon_x=0;
+ m_mon_y=0;
+
+ HINSTANCE h=LoadLibrary("user32.dll");
+ if (h) {
+ HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT,DWORD)) GetProcAddress(h,"MonitorFromPoint");
+ HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
+ HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags)=(HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
+ BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR,LPMONITORINFO)) GetProcAddress(h,"GetMonitorInfoA");
+ if (Mfp && Mfr && Mfw && Gmi) {
+ RECT r;
+ GetWindowRect(hwnd,&r);
+ HMONITOR hm=Mfr(&r,NULL);
+ if(hm) {
+ HINSTANCE hdd = LoadLibrary("ddraw.dll");
+ if(hdd) {
+ typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA)(GUID FAR *, LPSTR, LPSTR, LPVOID, HMONITOR);
+ typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEX)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
+ LPDIRECTDRAWENUMERATEEX lpDDEnumEx;
+ lpDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hdd,"DirectDrawEnumerateExA");
+ if (lpDDEnumEx) {
+ m_monitor_to_find=hm;
+ lpDDEnumEx(&DDEnumCallbackEx, this, DDENUM_ATTACHEDSECONDARYDEVICES|DDENUM_NONDISPLAYDEVICES);
+ if(m_found_devguid) {
+ MONITORINFOEX mi;
+ memset(&mi,0,sizeof(mi));
+ mi.cbSize=sizeof(mi);
+ if (Gmi(hm,&mi)) {
+ m_mon_x=mi.rcMonitor.left;
+ m_mon_y=mi.rcMonitor.top;
+ }
+ }
+ }
+ FreeLibrary(hdd);
+ }
+ }
+ }
+ FreeLibrary(h);
+ }
+}
+
+int VideoOutput::get_latency()
+{
+ return vid_vsync?15:0;
+}
+
+#undef GetSystemMetrics
+int VideoOutput::class_refcnt=0;
+
+void VideoOutput::adjustAspect(RECT &rd)
+{
+ if (vid_aspectadj)
+ {
+ int outh=rd.bottom-rd.top;
+ int outw=rd.right-rd.left;
+
+ int newh=(int)((aspect*height*outw)/(double)width);
+ int neww=(int)((width*outh)/(height*aspect));
+
+ if (outh > newh) // black bars on top and bottom
+ {
+ int d=outh - newh;
+ rd.top+=d/2;
+ rd.bottom-=d-d/2;
+ }
+ else if (outw > neww) // black bars on left and right
+ {
+ int d=outw - neww;
+ rd.left+=d/2;
+ rd.right-=d-d/2;
+ }
+ }
+}
+
+LRESULT CALLBACK VideoOutput::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WM_CREATE)
+ {
+ SetWindowLong(hwnd,GWL_USERDATA,(long)((CREATESTRUCT *)lParam)->lpCreateParams);
+ ShowWindow(hwnd,SW_SHOW);
+ if (GetParent(hwnd))
+ {
+ RECT r;
+ GetClientRect(GetParent(hwnd),&r);
+ SetWindowPos(hwnd,NULL,0,0,
+ r.right,
+ r.bottom,
+ SWP_NOACTIVATE|SWP_NOZORDER);
+ }
+ return 0;
+ }
+
+ VideoOutput *_This=(VideoOutput*)GetWindowLong(hwnd,GWL_USERDATA);
+ if (_This) return _This->WindowProc(hwnd,uMsg,wParam,lParam);
+ else return DefWindowProc(hwnd,uMsg,wParam,lParam);
+}
+
+void VideoOutput::notifyBufferState(int bufferstate) /* 0-255*/
+{
+ m_bufferstate=bufferstate;
+#ifdef ACTIVEX_CONTROL
+ PostMessage( video_hwnd, STATUS_MSG, STATUS_PREBUFFER, bufferstate );
+#endif
+ if (!m_video_output) {
+ if(GetTickCount()-m_lastbufinvalid>500) {
+ InvalidateRect(video_hwnd,NULL,FALSE);
+ m_lastbufinvalid=GetTickCount();
+ }
+ }
+}
+
+LRESULT VideoOutput::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_TIMER:
+ case WM_WINDOWPOSCHANGING:
+ case WM_WINDOWPOSCHANGED:
+ case WM_SIZE:
+ case WM_MOVE:
+ case WM_MOVING:
+ if (uMsg == WM_TIMER && wParam == TIMER_OSD_ID) {
+ hideOSD();
+ return 0;
+ }
+ EnterCriticalSection(&m_cs);
+ if(m_video_output) m_video_output->timerCallback();
+ LeaveCriticalSection(&m_cs);
+ if (uMsg == WM_TIMER) return 0;
+ break;
+
+ case WM_LBUTTONDOWN:
+ if(is_fs)
+ osdHitTest(LOWORD(lParam),HIWORD(lParam),0);
+#ifdef ACTIVEX_CONTROL
+ SendMessage( video_hwnd, STATUS_MSG, STATUS_MOUSEPRESS, 1 );
+#endif
+ break;
+
+ case WM_PAINT:
+ {
+ if (m_video_output && m_video_output->onPaint(hwnd,(HDC)wParam)) return 0;
+ if (m_logo && !m_video_output)
+ {
+ PAINTSTRUCT p;
+ BeginPaint(hwnd,&p);
+
+ RECT r;
+ GetClientRect(hwnd,&r);
+
+ HDC out=p.hdc;
+
+ HDC dc=CreateCompatibleDC(NULL);
+ SelectObject(dc,m_logo);
+ int xp=(r.right-r.left-m_logo_w)/2;
+ int yp=(r.bottom-r.top-m_logo_h)/2;
+ BitBlt(out,xp,yp,m_logo_w,m_logo_h,dc,0,0,SRCCOPY);
+
+ int bs=m_bufferstate;
+ if (bs < 16) bs=16;
+
+
+ HGDIOBJ oldobj1=SelectObject(out,CreateSolidBrush(RGB(0,0,0)));
+ HGDIOBJ oldobj2=SelectObject(out,CreatePen(PS_SOLID,0,RGB(0,0,0)));
+ Rectangle(out,r.left,r.top,r.right,yp);
+ if (m_statusmsg)
+ Rectangle(out,r.left,yp+m_logo_h,r.right,r.bottom);
+ else
+ {
+ Rectangle(out,r.left,yp+m_logo_h+2+9,r.right,r.bottom);
+ Rectangle(out,xp + ((bs * (m_logo_w+2))>>8),yp+m_logo_h+2,r.right, yp+9+m_logo_h+2);
+ }
+ Rectangle(out,r.left,yp,xp-1,yp+m_logo_h+9+2);
+ Rectangle(out,xp+m_logo_w+1,yp,r.right,yp+m_logo_h+2);
+ DeleteObject(SelectObject(out,oldobj2));
+ DeleteObject(SelectObject(out,oldobj1));
+
+ if (m_statusmsg)
+ {
+ RECT subr={0,yp+m_logo_h+2,r.right,r.bottom};
+ SetTextColor(out,RGB(255,255,255));
+ SetBkMode(out,TRANSPARENT);
+ DrawText(out,m_statusmsg,-1,&subr,DT_TOP|DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
+ }
+ else
+ {
+ yp+=m_logo_h+2;
+ if (bs)
+ {
+ HGDIOBJ oldobj1=SelectObject(out,CreateSolidBrush(RGB(128,128,128)));
+ HGDIOBJ oldobj2=SelectObject(out,CreatePen(PS_SOLID,0,RGB(255,255,255)));
+ Rectangle(out,xp-1,yp,xp + ((bs * (m_logo_w+2))>>8), yp+9);
+ DeleteObject(SelectObject(out,oldobj2));
+ DeleteObject(SelectObject(out,oldobj1));
+ }
+ }
+ DeleteDC(dc);
+ EndPaint(hwnd,&p);
+ return 0;
+ }
+ }
+ break;
+
+ case WM_USER+0x1:
+ m_need_change=1;
+ break;
+
+#ifdef ACTIVEX_CONTROL
+ case STATUS_MSG:
+ SendStatus( wParam, lParam );
+ break;
+#endif
+
+ case WM_KEYDOWN:
+ if(wParam==27 && is_fs) remove_fullscreen();
+ break;
+
+ case WM_MOUSEMOVE:
+ if(is_fs) {
+ if (ignore_mousemove_count>0) {
+ ignore_mousemove_count--;
+ }
+ else if (abs(osdLastMouseX - LOWORD(lParam)) + abs(osdLastMouseY - HIWORD(lParam)) > 1) {
+ KillTimer(hwnd, TIMER_OSD_ID);
+ showOSD();
+ SetTimer(hwnd, TIMER_OSD_ID, 2000, NULL);
+
+ if (wParam & MK_LBUTTON)
+ osdHitTest(LOWORD(lParam),HIWORD(lParam),1);
+ else
+ osdHitTest(LOWORD(lParam),HIWORD(lParam),-1);
+ }
+ osdLastMouseX = LOWORD(lParam);
+ osdLastMouseY = HIWORD(lParam);
+ }
+ break;
+ }
+ if (m_msgcallback)
+ {
+ return m_msgcallback(m_msgcallback_tok,hwnd, uMsg, wParam, lParam);
+ }
+
+ return (DefWindowProc(hwnd, uMsg, wParam, lParam));
+}
+
+VideoOutput::VideoOutput(HWND parent_hwnd, int initxpos, int initypos)
+{
+ curSubtitle=NULL;
+ m_statusmsg=0;
+ m_bufferstate=0;
+ m_msgcallback=0;
+ m_msgcallback_tok=0;
+ video_hwnd=video_parent_hwnd=0;
+ decoder=0;
+
+ vid_aspectadj=true;
+ vid_overlays=true;
+ vid_ddraw=true;
+ vid_vsync=true;
+ aspect=1.0;
+ m_need_change=false;
+
+ width=height=flip=uyvy_output=yuy2_output=is_fs=ignore_mousemove_count=show_osd=0;
+ oldfsparent=0;
+ memset(&oldfsrect,0,sizeof(oldfsrect));
+ memset(&lastfsrect,0,sizeof(lastfsrect));
+ oldfsstyle=0;
+
+ m_video_output=NULL;
+
+ osdFontText=NULL;
+ osdFontSymbol=NULL;
+ osdProgressBrushBg=NULL;
+ osdProgressBrushFg=NULL;
+ osdProgressPenBg=NULL;
+ osdProgressPenFg=NULL;
+ osdProgressPenBgHilite=NULL;
+ osdBlackBrush=NULL;
+ osdMemDC=NULL;
+ osdMemBM=NULL;
+ osdOldBM=NULL;
+ osdMemBMW=0;
+ osdMemBMH=0;
+ osdLastMouseX=-1;
+ osdLastMouseY=-1;
+
+ for (int i=0; i<NUM_WIDGETS; i++)
+ SetRect(&ctrlrect[i], 0, 0, 0, 0);
+ ctrlrects_ready = 0;
+
+ resetSubtitle();
+
+ WNDCLASS wc={0,};
+
+ wc.hCursor=LoadCursor(NULL,IDC_ARROW);
+ wc.lpfnWndProc = WndProc;
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.lpszClassName = "NSVplay";
+ LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),};
+ wc.hbrBackground=CreateBrushIndirect(&lb);
+ if (!class_refcnt) RegisterClass(&wc);
+ class_refcnt++;
+
+ m_logo=(HBITMAP)LoadImage(g_hInstance,MAKEINTRESOURCE(g_bitmap_id),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
+ BITMAP bm;
+ GetObject(m_logo, sizeof(BITMAP), &bm);
+ m_logo_w=bm.bmWidth;
+ m_logo_h=bm.bmHeight;
+ if(m_logo_h<0) m_logo_h=-m_logo_h;
+
+ InitializeCriticalSection(&m_cs);
+
+ video_hwnd=CreateWindowEx(0,wc.lpszClassName, "NSV Player",parent_hwnd?WS_CHILD:(WS_OVERLAPPEDWINDOW&(~WS_MAXIMIZEBOX)),
+ initxpos,initypos,320,200,
+ parent_hwnd, NULL,wc.hInstance,(void*)this);
+
+ video_parent_hwnd=parent_hwnd;
+
+ m_lastbufinvalid=0;
+
+#ifdef ACTIVEX_CONTROL
+ m_firstframe = 1;
+#endif
+}
+
+VideoOutputChild *VideoOutput::createVideoOutput(int n) {
+ if(!vid_overlays && !vid_ddraw) vid_overlays=true;
+
+ if(!vid_overlays) n++;
+ if(n==0) return new OverlayVideoOutput();
+ if(!vid_ddraw) n++;
+ if(n==1) return new DDrawVideoOutput();
+
+ return 0;
+}
+
+int VideoOutput::open(int w, int h, int vflip, double aspectratio, unsigned int fmt)
+{
+ EnterCriticalSection(&m_cs);
+ delete(m_video_output);
+ m_video_output=NULL;
+
+ if (!w) w=320;
+ if (!h) h=240;
+ width=w;
+ height=h;
+ flip=vflip;
+ type=fmt;
+ is_fs=0;
+ ignore_mousemove_count=0;
+ show_osd=0;
+ aspect=aspectratio;
+
+ for(int i=0;m_video_output=createVideoOutput(i);i++) {
+ if(m_video_output->create(this,w,h,fmt,vflip,aspectratio)) {
+ LeaveCriticalSection(&m_cs);
+ if (!GetParent(video_hwnd)) {
+ RECT r,r2;
+ int ow=width,oh=height;
+ if (aspect > 0.001)
+ {
+ if (aspect < 1.0) ow=(int)(ow/aspect);
+ else oh=(int)(oh*aspect);
+ }
+ GetWindowRect(video_hwnd,&r);
+ GetClientRect(video_hwnd,&r2);
+ SetWindowPos(video_hwnd,NULL,0,0,
+ ow+(r.right-r.left)-(r2.right-r2.left),
+ oh+(r.bottom-r.top)-(r2.bottom-r2.top),
+ SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+ }
+ return 0;
+ }
+ delete(m_video_output);
+ }
+ LeaveCriticalSection(&m_cs);
+ return 1;
+}
+
+void VideoOutput::draw(void *frame)
+{
+ if (!m_video_output || !frame) return;
+ if ((m_video_output && m_video_output->needChange()) || m_need_change) {
+ open(width,height,flip,aspect,type);
+ m_need_change=0;
+ }
+#ifdef ACTIVEX_CONTROL
+ if ( m_firstframe ) {
+ m_firstframe = 0;
+ PostMessage( video_hwnd, STATUS_MSG, STATUS_FIRSTFRAME, 1 );
+ }
+#endif
+ if (m_video_output) m_video_output->displayFrame((const char *)frame,0,0);
+}
+
+VideoOutput::~VideoOutput()
+{
+ free(m_statusmsg);
+ delete(m_video_output);
+ DestroyWindow(video_hwnd);
+ if (!--class_refcnt) UnregisterClass("NSVplay",GetModuleHandle(NULL));
+ if(osdFontText) DeleteObject(osdFontText);
+ if(osdFontSymbol) DeleteObject(osdFontSymbol);
+ if(osdProgressBrushBg) DeleteObject(osdProgressBrushBg);
+ if(osdProgressBrushFg) DeleteObject(osdProgressBrushFg);
+ if(osdBlackBrush ) DeleteObject(osdBlackBrush );
+ if(osdProgressPenBg ) DeleteObject(osdProgressPenBg );
+ if(osdProgressPenFg ) DeleteObject(osdProgressPenFg );
+ if(osdProgressPenBgHilite) DeleteObject(osdProgressPenBgHilite);
+ if(osdMemDC) {
+ SelectObject(osdMemDC,osdOldBM); // delete our doublebuffer
+ DeleteDC(osdMemDC);
+ }
+ if(osdMemBM) DeleteObject(osdMemBM);
+
+
+ DeleteCriticalSection(&m_cs);
+}
+
+void VideoOutput::close()
+{
+ delete(m_video_output);
+ m_video_output=NULL;
+}
+
+void VideoOutput::getViewport(RECT *r, HWND wnd, int full) {
+ POINT *p=NULL;
+ RECT *sr=NULL;
+ if (p || sr || wnd) {
+ HINSTANCE h=LoadLibrary("user32.dll");
+ if (h) {
+ HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT,DWORD)) GetProcAddress(h,"MonitorFromPoint");
+ HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
+ HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags)=(HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
+ BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR,LPMONITORINFO)) GetProcAddress(h,"GetMonitorInfoA");
+ if (Mfp && Mfr && Mfw && Gmi) {
+ HMONITOR hm = NULL;
+ if (p)
+ hm=Mfp(*p,MONITOR_DEFAULTTONULL);
+ else if (sr)
+ hm=Mfr(sr,MONITOR_DEFAULTTONULL);
+ else if (wnd)
+ hm=Mfw(wnd,MONITOR_DEFAULTTONULL);
+ if (hm) {
+ MONITORINFOEX mi;
+ memset(&mi,0,sizeof(mi));
+ mi.cbSize=sizeof(mi);
+
+ if (Gmi(hm,&mi)) {
+ if(!full) *r=mi.rcWork;
+ else *r=mi.rcMonitor;
+ FreeLibrary(h);
+ return;
+ }
+ }
+ }
+ FreeLibrary(h);
+ }
+ }
+ if (full)
+ { // this might be borked =)
+ r->top=r->left=0;
+ r->right=::GetSystemMetrics(SM_CXSCREEN);
+ r->bottom=::GetSystemMetrics(SM_CYSCREEN);
+ }
+ else
+ {
+ SystemParametersInfo(SPI_GETWORKAREA,0,r,0);
+ }
+}
+
+void VideoOutput::fullscreen()
+{
+ if (is_fs) return;
+ if(!m_video_output) return;
+ is_fs=1;
+ ignore_mousemove_count=2;
+
+ oldfsparent=GetParent(video_hwnd);
+ oldfsstyle=GetWindowLong(video_hwnd,GWL_STYLE);
+ if (!oldfsparent) GetWindowRect(video_hwnd,&oldfsrect);
+ else GetClientRect(video_hwnd,&oldfsrect);
+ getViewport(&lastfsrect,video_hwnd,1);
+
+ SetParent(video_hwnd,NULL);
+ SetWindowLong(video_hwnd,GWL_STYLE,WS_POPUP|WS_VISIBLE);
+ SetWindowPos(video_hwnd, HWND_TOPMOST, lastfsrect.left, lastfsrect.top, lastfsrect.right-lastfsrect.left, lastfsrect.bottom-lastfsrect.top, SWP_DRAWFRAME);
+ SetFocus(video_hwnd);
+
+ resetSubtitle();
+
+ //showOSD();
+
+ //SetCursor(NULL);
+}
+
+void VideoOutput::getOutputSize(int *w, int *h)
+{
+ RECT r2;
+ GetClientRect(video_hwnd,&r2);
+ *w=r2.right-r2.left;
+ *h=r2.bottom-r2.top;
+}
+
+void VideoOutput::setOutputSize(int w, int h)
+{
+ RECT r,r2;
+ GetWindowRect(video_hwnd,&r);
+ GetClientRect(video_hwnd,&r2);
+ SetWindowPos(video_hwnd, 0, 0,0,
+ w+(r.right-r.left)-(r2.right-r2.left),
+ h+(r.bottom-r.top)-(r2.bottom-r2.top),
+ SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
+}
+
+void VideoOutput::remove_fullscreen()
+{
+ if(!is_fs) return;
+
+ SetParent(video_hwnd,oldfsparent);
+ SetWindowLong(video_hwnd,GWL_STYLE,oldfsstyle);
+ // note: when returning from fullscreen *on a secondary monitor*,
+ // be careful how you set the new window Z order.
+ // nsvplay.exe: only HWND_NOTOPMOST works
+ // nsvplayX.exe: only HWND_TOP works
+ SetWindowPos(video_hwnd, oldfsparent ? HWND_TOP : HWND_NOTOPMOST, oldfsrect.left, oldfsrect.top, oldfsrect.right-oldfsrect.left, oldfsrect.bottom-oldfsrect.top, SWP_FRAMECHANGED);
+ SetFocus(oldfsparent ? oldfsparent : video_hwnd);
+
+ is_fs=0;
+ show_osd=0;
+ ctrlrects_ready=0;
+ resetSubtitle();
+
+ hideOSD();
+}
+
+int VideoOutput::is_fullscreen()
+{
+ return is_fs;
+}
+
+void VideoOutput::showStatusMsg(const char *text)
+{
+ m_statusmsg=_strdup(text);
+ InvalidateRect(video_hwnd,NULL,TRUE);
+}
+
+void VideoOutput::drawSubtitle(SubsItem *item)
+{
+ if(!item) {
+ if(curSubtitle) {
+ m_video_output->drawSubtitle(NULL);
+ curSubtitle=NULL;
+ }
+ return;
+ }
+
+ if(curSubtitle==item) return;
+
+ curSubtitle=item;
+
+ m_video_output->drawSubtitle(curSubtitle);
+}
+
+void VideoOutput::resetSubtitle()
+{
+ curSubtitle=NULL;
+ if(m_video_output) m_video_output->resetSubtitle();
+}
+
+void VideoOutput::showOSD() {
+ if(OSD_ENABLED && m_video_output) {
+ KillTimer(video_hwnd, TIMER_OSD_ID);
+ if (!show_osd)
+ m_video_output->showOSD();
+ SetTimer(video_hwnd, TIMER_OSD_ID, 2000, NULL);
+ show_osd = 1;
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ }
+}
+
+void VideoOutput::hideOSD() {
+ if(OSD_ENABLED && m_video_output) {
+ KillTimer(video_hwnd, TIMER_OSD_ID);
+ m_video_output->hideOSD();
+ show_osd = 0;
+ SetCursor(NULL);
+ }
+}
+
+void VideoOutput::drawOSD(HDC hdc, RECT *rg) {
+ if(m_video_output && show_osd) {
+
+ if (!osdMemDC ) osdMemDC = CreateCompatibleDC(hdc);
+ if (!osdFontText) osdFontText=CreateFont(OSD_TEXT_SIZE,0,0,0,FW_SEMIBOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Arial");
+ if (!osdFontSymbol) osdFontSymbol=CreateFont(OSD_TEXT_SIZE,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,SYMBOL_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH,"Webdings");
+ if (!osdProgressBrushBg) osdProgressBrushBg = CreateSolidBrush(RGB(OSD_VOL_BKCOL_R,OSD_VOL_BKCOL_G,OSD_VOL_BKCOL_B));
+ if (!osdProgressBrushFg) osdProgressBrushFg = CreateSolidBrush(RGB(OSD_VOL_COL_R,OSD_VOL_COL_G,OSD_VOL_COL_B));
+ if (!osdBlackBrush ) osdBlackBrush = CreateSolidBrush(RGB(0,0,0));//OV_COL_R,OV_COL_G,OV_COL_B));
+ if (!osdProgressPenBg ) osdProgressPenBg = CreatePen(PS_SOLID,0,RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
+ if (!osdProgressPenFg ) osdProgressPenFg = CreatePen(PS_NULL,0,RGB(0,0,0));
+ if (!osdProgressPenBgHilite) osdProgressPenBgHilite = CreatePen(PS_SOLID,0,RGB(OSD_TEXT_R_HILITE,OSD_TEXT_G_HILITE,OSD_TEXT_B_HILITE));
+
+ COLORREF fg = GetTextColor(osdMemDC);
+ COLORREF bg = GetBkColor(osdMemDC);
+ SetTextColor(osdMemDC, RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
+ SetBkColor(osdMemDC, RGB(0,0,0));//OV_COL_R,OV_COL_G,OV_COL_B));
+
+ HGDIOBJ oldfont = SelectObject(osdMemDC, osdFontText);
+ HGDIOBJ oldbrush = SelectObject(osdMemDC, osdProgressBrushBg);
+ HGDIOBJ oldpen = SelectObject(osdMemDC, osdProgressPenBg);
+
+ RECT fullr;
+ GetClientRect(video_hwnd,&fullr);
+ ClientToScreen(video_hwnd,(LPPOINT)&fullr);
+ ClientToScreen(video_hwnd,((LPPOINT)&fullr) + 1);
+ // transform coords from windows desktop coords (where 0,0==upper-left corner of the primary monitor)
+ // to the coords for the monitor we're displaying on:
+ fullr.top -= m_video_output->m_mon_y;
+ fullr.left -= m_video_output->m_mon_x;
+ fullr.right -= m_video_output->m_mon_x;
+ fullr.bottom -= m_video_output->m_mon_y;
+
+ if (!ctrlrects_ready) {
+ ctrlrects_ready = 1;
+
+ int net_width = 0;
+ int max_height = 0;
+ int streaming = (decoder && decoder->getlen()==-1) ? 1 : 0;
+
+ for (int i=0; i<NUM_WIDGETS; i++) {
+ if (streaming && (i==CTRL_PROGRESS || i==CTRL_PROGRESSTEXT || i==CTRL_PROGRESSSPACER || i==CTRL_FFWD || i==CTRL_REW)) {
+ // disable progress bar + seek arrows when the NSV is a stream
+ ctrlrect[i].right = -1;
+ continue;
+ }
+ else if (g_ctrl_force_width[i] != 0) {
+ SetRect(&ctrlrect[i], 0, 0, g_ctrl_force_width[i], 0);
+ }
+ else {
+ SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
+ SetRect(&ctrlrect[i], 0, 0, 256, 256);
+ ctrlrect[i].bottom = DrawText(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE|DT_CALCRECT);
+ }
+ net_width += ctrlrect[i].right - ctrlrect[i].left;
+ max_height = max(max_height, ctrlrect[i].bottom - ctrlrect[i].top);
+ }
+
+ // now we know the size of all the controls; now place them.
+ int x = (fullr.right + fullr.left)/2 - net_width/2;
+ SetRect(&ctrlrect_all, 0, 0, 0, 0);
+ for (i=0; i<NUM_WIDGETS; i++)
+ {
+ if (ctrlrect[i].right >= 0) // if control is not disabled...
+ {
+ int this_width = ctrlrect[i].right - ctrlrect[i].left;
+ int this_height = ctrlrect[i].bottom - ctrlrect[i].top ;
+ if (this_height==0) this_height = max_height*2/3;// progress bars
+ ctrlrect[i].top = max_height/2 - this_height/2;
+ ctrlrect[i].bottom = max_height/2 + this_height/2;
+ ctrlrect[i].left = x;
+ ctrlrect[i].right = x + this_width;
+ if (ctrlrect_all.bottom==0) {
+ ctrlrect_all.top = ctrlrect[i].top ;
+ ctrlrect_all.bottom = ctrlrect[i].bottom;
+ }
+ else {
+ ctrlrect_all.top = min(ctrlrect_all.top , ctrlrect[i].top );
+ ctrlrect_all.bottom = max(ctrlrect_all.bottom, ctrlrect[i].bottom);
+ }
+ x += this_width;
+ }
+ }
+ }
+
+ int w = fullr.right - fullr.left;
+ int h = ctrlrect_all.bottom - ctrlrect_all.top;
+ if (!osdMemBM || osdMemBMW != w || osdMemBMH != h) {
+ if (osdMemBM) {
+ SelectObject(osdMemDC,osdOldBM);
+ DeleteObject(osdMemBM);
+ }
+ osdMemBM = CreateCompatibleBitmap(hdc,w,h);
+ osdOldBM = (HBITMAP)SelectObject(osdMemDC, osdMemBM);
+ osdMemBMW = w;
+ osdMemBMH = h;
+ }
+
+ RECT temp;
+ SetRect(&temp, 0, 0, w, h);
+ FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
+
+ for (int i=0; i<NUM_WIDGETS; i++) {
+ if (g_ctrl_type[i] == CTRLTYPE_PROGRESS)
+ {
+ int progress = 0;
+ int max_progress = ctrlrect[i].right - ctrlrect[i].left;
+ switch(i)
+ {
+ case CTRL_VOL:
+ if (decoder)
+ progress = decoder->getvolume()*max_progress/255;
+ break;
+ case CTRL_PROGRESS:
+ if (decoder)
+ {
+ int len = decoder->getlen();
+ if (len>0)
+ progress = decoder->getpos()*max_progress/len;
+ }
+ break;
+ }
+
+ SelectObject(osdMemDC, osdProgressBrushBg);
+ SelectObject(osdMemDC, (i==osdLastClickItem) ? osdProgressPenBgHilite : osdProgressPenBg);
+ RoundRect(osdMemDC, ctrlrect[i].left, ctrlrect[i].top, ctrlrect[i].right, ctrlrect[i].bottom, 3, 3);
+ SelectObject(osdMemDC, osdProgressBrushFg);
+ SelectObject(osdMemDC, osdProgressPenFg);
+ Rectangle(osdMemDC, ctrlrect[i].left+1, ctrlrect[i].top+1, ctrlrect[i].left + progress, ctrlrect[i].bottom);
+ }
+ else if (g_ctrl_type[i] == CTRLTYPE_SYMBOL ||
+ g_ctrl_type[i] == CTRLTYPE_TEXT)
+ {
+ SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
+ SetTextColor(osdMemDC, (i==osdLastClickItem) ? RGB(OSD_TEXT_R_HILITE,OSD_TEXT_G_HILITE,OSD_TEXT_B_HILITE) : RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
+ DrawText(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE);
+ }
+ }
+
+ int x0 = fullr.left;
+ int y0 = fullr.bottom - (ctrlrect_all.bottom - ctrlrect_all.top);
+ BitBlt(hdc,x0,y0,w,h,osdMemDC,0,0,SRCCOPY);
+
+ // display stream title @ the top:
+#if (SHOW_STREAM_TITLE_AT_TOP)
+ if (decoder)
+ {
+ RECT temp;
+ SetRect(&temp, 0, 0, w, h);
+ FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
+
+ SelectObject(osdMemDC, osdFontText);
+ SetTextColor(osdMemDC, RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
+ char *t=decoder->getTitle();
+ char *buf=(char*)malloc(32+(t?strlen(t):0));
+
+ wsprintf(buf, "%s (%d kbps)", t?t:"", decoder->getBitrate()/1000);
+ char *p=buf;
+ while (*p)
+ {
+ if (*p == '_') *p=' ';
+ p++;
+ }
+ DrawText(osdMemDC, buf, -1, &temp, DT_SINGLELINE|DT_CENTER);
+ free(buf);
+
+ SelectObject(osdMemDC, osdFontSymbol);
+ DrawText(osdMemDC, "2r", -1, &temp, DT_SINGLELINE|DT_RIGHT);
+
+ int x0 = fullr.left;
+ int y0 = fullr.top;
+ BitBlt(hdc,x0,y0,w,h,osdMemDC,0,0,SRCCOPY);
+ }
+
+ SelectObject(osdMemDC, oldpen);
+ SelectObject(osdMemDC, oldbrush);
+ SelectObject(osdMemDC, oldfont);
+ SetTextColor(osdMemDC, fg);
+ SetBkColor(osdMemDC, bg);
+ }
+#endif
+
+}
+
+void VideoOutput::osdHitTest(int x, int y, int dragging)
+{
+ // dragging == -1: just a mousemove (no clicking)
+ // dragging == 0: user clicked
+ // dragging == 1: user clicked before, and is now dragging/moving mouse
+
+ if (dragging<1)
+ osdLastClickItem = -1;
+
+ // transform (x,y) from screen coords into coords relative to the memDC
+ y = y - ((lastfsrect.bottom - lastfsrect.top) - (ctrlrect_all.bottom - ctrlrect_all.top));
+
+ int i0 = 0;
+ int i1 = NUM_WIDGETS;
+ if (dragging==1) {
+ i0 = osdLastClickItem;
+ i1 = osdLastClickItem+1;
+ }
+
+ for (int i=i0; i<i1; i++)
+ {
+ if (dragging==1 || (x >= ctrlrect[i].left && x <= ctrlrect[i].right && y >= ctrlrect[i].top && y <= ctrlrect[i].bottom))
+ {
+ float t = (x - ctrlrect[i].left) / (float)(ctrlrect[i].right - ctrlrect[i].left);
+ if (t<0) t=0;
+ if (t>1) t=1;
+ if (dragging<1)
+ osdLastClickItem = i;
+
+ switch(i)
+ {
+ case CTRL_VOL:
+ if (decoder && dragging>=0) decoder->setvolume((int)(t*255));
+ return;
+ case CTRL_PROGRESS:
+ if (decoder && dragging>=0)
+ {
+ int len = decoder->getlen();
+ if (len > 0)
+ decoder->seek((int)(t*len));
+ }
+ return;
+ case CTRL_PAUSE:
+ if (decoder && dragging>=0) decoder->pause(1);
+ return;
+ case CTRL_PLAY:
+ if (decoder && dragging>=0) decoder->pause(0);
+ return;
+ case CTRL_STOP:
+ if (decoder && dragging>=0) {
+ decoder->pause(1);
+ remove_fullscreen();
+ }
+ return;
+ case CTRL_REW:
+ case CTRL_FFWD:
+ if (decoder && dragging>=0)
+ {
+ int pos = decoder->getpos();
+ int len = decoder->getlen();
+ if (len > 0)
+ {
+ if (i==CTRL_REW)
+ pos = max(0, pos-15000); // milliseconds to rewind
+ else
+ pos = min(len, pos+30000); // milliseconds to skip ahead
+ decoder->seek(pos);
+ }
+ }
+ return;
+ default:
+ if (dragging<1)
+ osdLastClickItem = -1;
+ break;
+ }
+ }
+ }
+
+ if (dragging==0)
+ remove_fullscreen();
+}
diff --git a/Src/nsv/nsvplay/video.h b/Src/nsv/nsvplay/video.h
new file mode 100644
index 00000000..535cee24
--- /dev/null
+++ b/Src/nsv/nsvplay/video.h
@@ -0,0 +1,160 @@
+#ifndef _VIDEO_H
+#define _VIDEO_H
+
+#include <windows.h>
+#include <ddraw.h>
+#include <multimon.h>
+
+#include "main.h"
+
+#define NUM_WIDGETS 11
+
+class VideoOutput;
+class SubsItem;
+
+class VideoOutputChild {
+public:
+ VideoOutputChild() { m_mon_x=m_mon_y=0; }
+ virtual ~VideoOutputChild() { }
+
+ virtual int create(VideoOutput *parent, int w, int h, unsigned int type, int flipit, double aspectratio)=0; //return 1 if ok
+ virtual int needChange()=0; //return 1 if need to renegociate video output
+
+ virtual int onPaint(HWND hwnd, HDC hdc) { return 0; } //return 1 if override
+ virtual void displayFrame(const char *buf, int size, int time)=0;
+
+ virtual void goFullScreen()=0;
+ virtual void removeFullScreen()=0;
+ virtual int showOSD() { return 0; }
+ virtual void hideOSD() { }
+
+ virtual void timerCallback() { }
+
+ virtual void setPalette(RGBQUAD *pal) { }
+
+ virtual void drawSubtitle(SubsItem *item) { }
+ virtual void resetSubtitle() { }
+
+ char m_szTest[512];
+
+ void update_monitor_coords(VideoOutput *parent);
+ int m_mon_x;
+ int m_mon_y;
+ int m_found_devguid;
+ GUID m_devguid;
+ HMONITOR m_monitor_to_find;
+};
+
+#include "vid_overlay.h"
+#include "vid_ddraw.h"
+
+class VideoOutput : public IVideoOutput {
+ public:
+ VideoOutput(HWND parent_hwnd=NULL, int initxpos=CW_USEDEFAULT, int initypos=CW_USEDEFAULT);
+ ~VideoOutput();
+ int open(int w, int h, int vflip, double aspectratio, unsigned int fmt);
+ void close();
+ void draw(void *frame);
+ void drawSubtitle(SubsItem *item);
+ void showStatusMsg(const char *text);
+ void notifyBufferState(int bufferstate); /* 0-255*/
+ int get_latency();
+ void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { m_msgcallback_tok=token; m_msgcallback=msgcallback; }
+ void setNSVDecoder(NSVDecoder *nsv_decoder) { decoder = nsv_decoder; }
+
+ void fullscreen();
+ void remove_fullscreen();
+ int is_fullscreen();
+
+ HWND getHwnd() { return video_hwnd; }
+
+ void adjustAspect(RECT &rd);
+
+ int vid_vsync;
+ int vid_aspectadj;
+ int vid_overlays;
+ int vid_ddraw;
+
+ void getViewport(RECT *r, HWND wnd, int full);
+ void setOutputSize(int w, int h);
+ void getOutputSize(int *w, int *h);
+
+ int osdShowing() { return show_osd; }
+ int osdReady() { return ctrlrects_ready; }
+ void showOSD();
+ void hideOSD();
+ void drawOSD(HDC hdc, RECT *r);
+ int getOSDbarHeight() { return (show_osd && ctrlrects_ready) ? (ctrlrect_all.bottom - ctrlrect_all.top) : 0; };
+
+ private:
+
+
+ LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ static int class_refcnt;
+
+ static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ HWND video_hwnd, video_parent_hwnd;
+ NSVDecoder *decoder;
+
+ double aspect;
+ int width, height, flip;
+ unsigned int type;
+ int uyvy_output,yuy2_output;
+ int i420_output;
+ int is_fs;
+ int ignore_mousemove_count;
+ int show_osd;
+ HWND oldfsparent;
+ RECT oldfsrect; // the old window rect, BEFORE fullscreen mode was entered
+ RECT lastfsrect; // the most recent bounding rect when in fullscreen mode
+ LONG oldfsstyle;
+
+ int m_bufferstate;
+ void resetSubtitle();
+ SubsItem *curSubtitle;
+
+ // ONSCREEN DISPLAY (osd):
+ HFONT osdFontText;
+ HFONT osdFontSymbol;
+ HGDIOBJ osdProgressBrushBg;
+ HGDIOBJ osdProgressBrushFg;
+ HGDIOBJ osdProgressPenBg;
+ HGDIOBJ osdProgressPenFg;
+ HGDIOBJ osdProgressPenBgHilite;
+ HGDIOBJ osdBlackBrush;
+ void osdHitTest(int x, int y, int dragging);
+ int osdLastClickItem;
+ HDC osdMemDC; // memory device context
+ HBITMAP osdMemBM; // memory bitmap (for memDC)
+ HBITMAP osdOldBM; // old bitmap (from memDC)
+ int osdMemBMW; // width of memory bitmap
+ int osdMemBMH; // height of memory bitmap
+ int osdLastMouseX; // for WM_MOUSEMOVE thresholding, so osd isn't spastic
+ int osdLastMouseY; // for WM_MOUSEMOVE thresholding, so osd isn't spastic
+ RECT ctrlrect[NUM_WIDGETS]; // relative to [i.e. (0,0) is] upper left corner of the black strip @ the bottom
+ RECT ctrlrect_all; // relative to [i.e. (0,0) is] upper left corner of the black strip @ the bottom
+ int ctrlrects_ready;
+
+ LRESULT (*m_msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ void *m_msgcallback_tok;
+
+ VideoOutputChild *m_video_output;
+ VideoOutputChild *createVideoOutput(int n);
+
+ CRITICAL_SECTION m_cs;
+ char *m_statusmsg;
+
+ int m_need_change;
+
+ HBITMAP m_logo;
+ int m_logo_w, m_logo_h;
+
+ DWORD m_lastbufinvalid;
+
+#ifdef ACTIVEX_CONTROL
+ int m_firstframe;
+#endif
+};
+
+#endif \ No newline at end of file
diff --git a/Src/nsv/nsvplay/vp3stub.cpp b/Src/nsv/nsvplay/vp3stub.cpp
new file mode 100644
index 00000000..8f6776c7
--- /dev/null
+++ b/Src/nsv/nsvplay/vp3stub.cpp
@@ -0,0 +1,120 @@
+#include "main.h"
+#include "../../vp32/include/duck_dxl.h"
+#include "vfw.h"
+
+extern "C" {
+ void GetImageBufs(DXL_XIMAGE_HANDLE x, YV12_PLANES *p);
+};
+
+int vp3_postprocess=0;
+int vp3_targetcpu=0;
+
+class VP3_Decoder : public IVideoDecoder {
+ public:
+ VP3_Decoder(int w, int h, int uvflip);
+ ~VP3_Decoder();
+ int decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf);
+ void flush() { }
+
+ private:
+ int m_uvflip;
+ int l_tcpu, l_pp;
+ static int init;
+ DXL_XIMAGE_HANDLE xim;
+ YV12_PLANES vidbufdec;
+};
+
+int VP3_Decoder::init;
+
+VP3_Decoder::VP3_Decoder(int w, int h, int uvflip)
+{
+ l_tcpu=-1;
+ l_pp=-1;
+ if (!init)
+ {
+ init=1;
+ DXL_InitVideoEx(1,1);
+ }
+ m_uvflip=uvflip;
+ vidbufdec.y.baseAddr=0;
+ xim = DXL_AlterXImage( NULL, (unsigned char *)"" ,MAKEFOURCC('V','P','3','1'), DXRGBNULL,w,h);
+}
+
+VP3_Decoder::~VP3_Decoder()
+{
+ if ( xim ) DXL_DestroyXImage( xim);
+}
+
+
+int VP3_Decoder::decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf)
+{
+ BYTE *data=(BYTE*)in;
+
+ if (!xim) return -1;
+
+ *out_type=NSV_MAKETYPE('Y','V','1','2');
+
+ if (vp3_postprocess != l_pp || vp3_targetcpu != l_tcpu)
+ {
+ l_pp=vp3_postprocess;
+ l_tcpu=vp3_targetcpu;
+ if (l_pp)
+ {
+ int v=l_tcpu;
+ if (v < 1) v=1;
+ if (v > 100) v=100;
+ vp31_SetParameter(xim,1, v);
+ vp31_SetParameter(xim,0, 9);
+ }
+ else
+ {
+ vp31_SetParameter(xim,1, 0);
+ vp31_SetParameter(xim,0, 0);
+ }
+ }
+
+ DXL_AlterXImageData( xim, data);
+ DXL_SetXImageCSize(xim, in_len);
+
+ *is_kf=!(!in_len || data[0] > 0x7f);
+
+ *out=NULL;
+
+ if ((need_kf && !*is_kf) || !in_len)
+ {
+ return 0;
+ }
+
+ if (!DXL_dxImageToVScreen( xim, NULL))
+ {
+ GetImageBufs(xim,&vidbufdec);
+ if (m_uvflip)
+ {
+ YV12_PLANE tmp=vidbufdec.v;
+ vidbufdec.v=vidbufdec.u;
+ vidbufdec.u=tmp;
+ }
+ *out=&vidbufdec;
+ return 0;
+ }
+
+ return -1;
+}
+
+IVideoDecoder *VP3_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip)
+{
+ if (fmt == NSV_MAKETYPE('V','P','3',' ') || fmt == NSV_MAKETYPE('V','P','3','1'))
+ {
+ *flip=1;
+ return new VP3_Decoder(w,h,fmt == NSV_MAKETYPE('V','P','3',' '));
+ }
+ return NULL;
+}
diff --git a/Src/nsv/nsvplay/vp3stub.h b/Src/nsv/nsvplay/vp3stub.h
new file mode 100644
index 00000000..dacc9663
--- /dev/null
+++ b/Src/nsv/nsvplay/vp3stub.h
@@ -0,0 +1,11 @@
+#ifndef _VP3STUB_H_
+#define _VP3STUB_H_
+
+#include "main.h"
+
+extern int vp3_postprocess;
+extern int vp3_targetcpu;
+
+IVideoDecoder *VP3_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip);
+
+#endif \ No newline at end of file
diff --git a/Src/nsv/nsvplay/vp5stub.cpp b/Src/nsv/nsvplay/vp5stub.cpp
new file mode 100644
index 00000000..9c49c359
--- /dev/null
+++ b/Src/nsv/nsvplay/vp5stub.cpp
@@ -0,0 +1,112 @@
+#include <windows.h>
+#include "../nsvlib.h"
+#include "../dec_if.h"
+
+#include "vfw.h"
+
+class VP5_Decoder : public IVideoDecoder {
+ public:
+ VP5_Decoder(int w, int h);
+ ~VP5_Decoder();
+ int decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf);
+ void flush() { }
+
+ int m_err;
+
+ private:
+ int width,height;
+ BITMAPINFO vp5_bmo,vp5_bmi;
+ HIC vp5_hic;
+ unsigned char *vidbufdec;
+};
+
+VP5_Decoder::VP5_Decoder(int w, int h)
+{
+ width=w;
+ height=h;
+ m_err=0;
+ vp5_hic=0;
+ vidbufdec=(unsigned char*)malloc(sizeof(YV12_PLANES) + w*h*3/2);
+ // init vp5 decode
+ memset((void *) &vp5_bmi,0,sizeof(BITMAPINFO));
+ memset((void *) &vp5_bmo,0,sizeof(BITMAPINFO));
+
+ vp5_bmi.bmiHeader.biCompression = mmioFOURCC('V','P','5','0');
+ vp5_bmi.bmiHeader.biHeight=h;
+ vp5_bmi.bmiHeader.biWidth =w;
+
+ vp5_bmo.bmiHeader.biCompression = mmioFOURCC('Y','V','1','2');
+ vp5_bmo.bmiHeader.biHeight=h;
+ vp5_bmo.bmiHeader.biWidth =w;
+ vp5_bmo.bmiHeader.biBitCount = 12;
+
+ vp5_hic = ICOpen(ICTYPE_VIDEO, vp5_bmi.bmiHeader.biCompression, ICMODE_DECOMPRESS);
+ vp5_bmo.bmiHeader.biHeight*=-1;
+ if(!vp5_hic || ICERR_OK !=ICDecompressBegin(vp5_hic, &vp5_bmi, &vp5_bmo))
+ {
+ m_err=1;
+ return;
+ }
+}
+
+VP5_Decoder::~VP5_Decoder()
+{
+ if (vp5_hic)
+ {
+ ICDecompressEnd(vp5_hic);
+ ICClose(vp5_hic);
+ }
+ free(vidbufdec);
+}
+
+
+int VP5_Decoder::decode(int need_kf,
+ void *in, int in_len,
+ void **out, // out is set to a pointer to data
+ unsigned int *out_type, // 'Y','V','1','2' is currently defined
+ int *is_kf)
+{
+ *out_type=NSV_MAKETYPE('Y','V','1','2');
+ vp5_bmi.bmiHeader.biSizeImage = in_len;
+ if(ICERR_OK == ICDecompress(vp5_hic,0,(BITMAPINFOHEADER *) &vp5_bmi, (char*)in,(BITMAPINFOHEADER *) &vp5_bmo, (char*)vidbufdec+sizeof(YV12_PLANES)))
+ {
+ *is_kf=!(!in_len || ((unsigned char *)in)[0] > 0x7f);
+ if (need_kf && !*is_kf)
+ {
+ return 0;
+ }
+ YV12_PLANES *image_vbd=(YV12_PLANES *)vidbufdec;
+ image_vbd->y.baseAddr=(unsigned char *)(image_vbd+1);
+ image_vbd->v.baseAddr=((unsigned char *)(image_vbd+1)) + width*height;
+ image_vbd->u.baseAddr=((unsigned char *)(image_vbd+1)) + width*height*5/4;
+ image_vbd->y.rowBytes=width;
+ image_vbd->v.rowBytes=width/2;
+ image_vbd->u.rowBytes=width/2;
+ *out=(void*)vidbufdec;
+
+ return 0;
+ }
+
+ return -1;
+
+}
+
+IVideoDecoder *VP5_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip)
+{
+ if (fmt == NSV_MAKETYPE('V','P','5','0'))
+ {
+ *flip=0;
+ VP5_Decoder *a=new VP5_Decoder(w,h);
+ if (a->m_err)
+ {
+ delete a;
+ return NULL;
+ }
+ return a;
+ }
+ return NULL;
+}
diff --git a/Src/nsv/nsvplay/vp5stub.h b/Src/nsv/nsvplay/vp5stub.h
new file mode 100644
index 00000000..55124650
--- /dev/null
+++ b/Src/nsv/nsvplay/vp5stub.h
@@ -0,0 +1,8 @@
+#ifndef _VP5STUB_H_
+#define _VP5STUB_H_
+
+#include "main.h"
+
+IVideoDecoder *VP5_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip);
+
+#endif \ No newline at end of file
diff --git a/Src/nsv/nsvplay/wndmenu.h b/Src/nsv/nsvplay/wndmenu.h
new file mode 100644
index 00000000..17677901
--- /dev/null
+++ b/Src/nsv/nsvplay/wndmenu.h
@@ -0,0 +1,388 @@
+#ifndef _WNDMENU_H_
+#define _WNDMENU_H_
+
+//need to define WNDMENU_CAPTION to the caption
+// you can define these to remove menu functionality
+//WNDMENU_NOABOUT // no about header
+//WNDMENU_NOVOLUME // no volume submenu
+//WNDMENU_NOINFO // no info submenu
+//WNDMENU_NOZOOM // no zoom 50/100/200/400
+//WNDMENU_NOZOOMFS // no zoom fullscreen
+//WNDMENU_NOOPTIONS // no options submenu
+//WNDMENU_NOPOSITIONSAVE // disables saving of position
+//WNDMENU_NOSUBTITLES // no subtitles submenu
+
+#ifndef ABS
+#define ABS(x) ((x)<0?-(x):(x))
+#endif
+
+
+static void _ReadConfigItemInt(char *name, int *value)
+{
+ HKEY hKey;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software\\Nullsoft\\NSVPlay",0,KEY_READ,&hKey) == ERROR_SUCCESS)
+ {
+ DWORD t,l=4;
+ int a;
+ if (RegQueryValueEx(hKey,name,NULL,&t,(unsigned char *)&a,&l ) == ERROR_SUCCESS && t == REG_DWORD)
+ *value=a;
+ RegCloseKey(hKey);
+ }
+}
+
+static void _WriteConfigItemInt(char *name, int value)
+{
+ HKEY hKey;
+ if (RegCreateKey(HKEY_LOCAL_MACHINE,"Software\\Nullsoft\\NSVPlay",&hKey) == ERROR_SUCCESS)
+ {
+ RegSetValueEx(hKey,name,0,REG_DWORD,(unsigned char*)&value,4);
+ RegCloseKey(hKey);
+ }
+}
+
+static void _InsertString(HMENU hMenu, int pos, char *string, int id, int checked=0, int disabled=0)
+{
+ MENUITEMINFO inf={sizeof(inf),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING,
+ (checked?MFS_CHECKED:0) | (disabled?MFS_DISABLED:MFS_ENABLED), id,
+ };
+ inf.dwTypeData = string;
+
+ InsertMenuItem(hMenu,pos,TRUE,&inf);
+}
+
+static void _InsertSep(HMENU hMenu, int pos)
+{
+ MENUITEMINFO inf={sizeof(inf),MIIM_TYPE,MFT_SEPARATOR };
+ InsertMenuItem(hMenu,pos,TRUE,&inf);
+}
+
+static HMENU _InsertSub(HMENU hMenu, int pos, char *string)
+{
+ MENUITEMINFO inf={sizeof(inf),MIIM_TYPE|MIIM_SUBMENU,MFT_STRING };
+ inf.dwTypeData = string;
+ inf.hSubMenu=CreatePopupMenu();
+ InsertMenuItem(hMenu,pos,TRUE,&inf);
+ return inf.hSubMenu;
+}
+
+
+#define MENUITEM_ABOUT 1
+#define MENUITEM_INFOBASE 10
+#define MENUITEM_VOLUMEBASE 100
+#define MENUITEM_VSYNC 120
+#define MENUITEM_ASPECTADJ 121
+#define MENUITEM_VOVERLAYS 122
+#define MENUITEM_VDDRAW 123
+#define MENUITEM_SOFTVOLMIX 124
+#define MENUITEM_ENABLESUBTITLES 125
+#define MENUITEM_SUBSIZEBASE 126 // 5 sizes
+
+#define MENUITEM_ZOOM50 112
+#define MENUITEM_ZOOM100 113
+#define MENUITEM_ZOOM200 114
+#define MENUITEM_ZOOM400 115
+#define MENUITEM_ZOOMFS 116
+
+#define MENUITEM_CLOSE 200
+
+#define MENUITEM_SUBSLANGBASE 300
+
+extern int g_audio_use_mixer;
+
+LRESULT my_wndcallback(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (token)
+ {
+ parms *parm=(parms*)token;
+#ifndef WNDMENU_NOPOSITIONSAVE
+ if (uMsg == WM_MOVE)
+ {
+ if (!parm->vidOut->is_fullscreen())
+ {
+ RECT r;
+ GetWindowRect(hwnd,&r);
+ _WriteConfigItemInt("xpos",r.left);
+ _WriteConfigItemInt("ypos",r.top);
+ }
+ }
+#endif
+ if(uMsg==WM_SETCURSOR && parm->vidOut->is_fullscreen()) {
+ SetCursor(NULL);
+ return TRUE;
+ }
+
+ if (uMsg == WM_RBUTTONUP && !parm->vidOut->is_fullscreen())
+ {
+ int pos=0;
+ HMENU hMenu=CreatePopupMenu();
+
+#ifndef WNDMENU_NOABOUT
+ _InsertString(hMenu,pos++,WNDMENU_CAPTION,MENUITEM_ABOUT);
+ _InsertSep(hMenu,pos++);
+#endif
+#ifndef WNDMENU_NOINFO
+ if (parm->decode)
+ {
+ char buf[1024];
+ int subpos=0;
+ int tmpid=MENUITEM_INFOBASE;
+ HMENU sub=_InsertSub(hMenu,pos++,"Info");
+
+ { // title
+ char *v=parm->decode->getTitle();
+ if (v && *v)
+ {
+ char *tmp=(char*)malloc(strlen(v)+32);
+ wsprintf(tmp,"Title: %s",v);
+ char *p=tmp;
+ while (*p)
+ {
+ if (*p == '_') *p=' ';
+ p++;
+ }
+ _InsertString(sub,subpos++,tmp,tmpid++);
+ free(tmp);
+ }
+ }
+
+
+ { // length
+ int lenms=parm->decode->getlen();
+ if (lenms != ~0)
+ {
+ wsprintf(buf,"Length: %d:%02d",lenms/60000,(lenms/1000)%60);
+ _InsertString(sub,subpos++,buf,tmpid++);
+ }
+ }
+
+
+ { // codecs
+ int a=(parm->decode->getBitrate()+500)/1000;
+ unsigned int a2=parm->decode->getFileSize();
+ if (a)
+ {
+ wsprintf(buf,"Bitrate: %dkbps",a);
+ if (a2 && a2 != ~0)
+ wsprintf(buf+strlen(buf)," (Total %u bytes)",a2);
+
+ _InsertString(sub,subpos++,buf,tmpid++);
+ }
+ else if (a2 && a2 != ~0)
+ {
+ wsprintf(buf,"Size: %u bytes",a2);
+ _InsertString(sub,subpos++,buf,tmpid++);
+ }
+
+ lstrcpy(buf,"Video: ");
+ char *p=buf+strlen(buf);
+ parm->decode->getVideoDesc(p);
+ if (*p) _InsertString(sub,subpos++,buf,tmpid++);
+
+ lstrcpy(buf,"Audio: ");
+ p=buf+strlen(buf);
+ parm->decode->getAudioDesc(p);
+ if (*p) _InsertString(sub,subpos++,buf,tmpid++);
+ }
+
+ {
+ const char *p=parm->decode->getServerHeader("Server");
+ if (p && *p)
+ {
+ lstrcpy(buf,"Server: ");
+ lstrcpyn(buf+strlen(buf),p,sizeof(buf)-strlen(buf)-2);
+ _InsertString(sub,subpos++,buf,tmpid++);
+ }
+ }
+ }
+#endif //WNDMENU_NOINFO
+#ifndef WNDMENU_NOVOLUME
+ if (parm->decode)
+ {
+ HMENU sub=_InsertSub(hMenu,pos++,"Volume");
+ int cv=(parm->decode->getvolume()*100)/255;
+ int x;
+ for (x=0;x<=10;x++)
+ {
+ char buf[64];
+ int a=100-x*10;
+ wsprintf(buf,"%d%%",a);
+ _InsertString(sub,x,buf,MENUITEM_VOLUMEBASE+x,cv >= a-5 && cv < a+5);
+ }
+ }
+#endif//WNDMENU_NOVOLUME
+#ifndef WNDMENU_NOOPTIONS
+ if (parm->vidOut)
+ {
+ HMENU sub=_InsertSub(hMenu,pos++,"Options");
+ int subpos=0;
+ _InsertString(sub,subpos++,"Synchronize video to refresh",MENUITEM_VSYNC,!!parm->vidOut->vid_vsync);
+ _InsertString(sub,subpos++,"Maintain video aspect ratio",MENUITEM_ASPECTADJ,!!parm->vidOut->vid_aspectadj);
+ _InsertString(sub,subpos++,"Allow video overlay",MENUITEM_VOVERLAYS,!!parm->vidOut->vid_overlays);
+ //_InsertString(sub,subpos++,"Allow DirectDraw acceleration",MENUITEM_VDDRAW,!!parm->vidOut->vid_ddraw);
+ _InsertString(sub,subpos++,"Use software volume mixing",MENUITEM_SOFTVOLMIX,!g_audio_use_mixer);
+ }
+#endif//WNDMENU_NOOPTIONS
+#if !defined(WNDMENU_NOZOOM) || !defined(WNDMENU_NOZOOMFS)
+ if (parm->decode && parm->vidOut)
+ {
+ HMENU sub=_InsertSub(hMenu,pos++,"Zoom");
+ int subpos=0;
+#ifndef WNDMENU_NOZOOM
+ int ow,oh;
+ int w=parm->decode->getWidth();
+ int h=parm->decode->getHeight();
+ parm->vidOut->getOutputSize(&ow,&oh);
+ _InsertString(sub,subpos++,"50%",MENUITEM_ZOOM50,ABS(w/2-ow)<4);
+ _InsertString(sub,subpos++,"100%",MENUITEM_ZOOM100,ABS(w-ow)<4);
+ _InsertString(sub,subpos++,"200%",MENUITEM_ZOOM200,ABS(w*2-ow)<4);
+ _InsertString(sub,subpos++,"400%",MENUITEM_ZOOM400,ABS(w/4-ow)<4);
+#ifndef WNDMENU_NOZOOMFS
+ _InsertSep(sub,subpos++);
+#endif
+#endif
+
+#ifndef WNDMENU_NOZOOMFS
+ _InsertString(sub,subpos++,"Fullscreen",MENUITEM_ZOOMFS,!!parm->vidOut->is_fullscreen());
+#endif//WNDMENU_NOZOOMFS
+ }
+#endif//WNDMENU_NOZOOM||WNDMENU_NOZOOMFS
+
+#ifndef WNDMENU_NOSUBTITLES
+ if(parm->decode)
+ {
+ HMENU sub=_InsertSub(hMenu,pos++,"Subtitles");
+ int subpos=0;
+ _InsertString(sub,subpos++,"Enabled",MENUITEM_ENABLESUBTITLES,parm->decode->subsEnabled());
+ _InsertSep(sub,subpos++);
+ HMENU sizesub=_InsertSub(sub,subpos++,"Font size");
+ int sizesubpos=0;
+ int fontsize=parm->decode->getSubsFontSize();
+ _InsertString(sizesub,sizesubpos++,"Largest",MENUITEM_SUBSIZEBASE,fontsize==20);
+ _InsertString(sizesub,sizesubpos++,"Larger",MENUITEM_SUBSIZEBASE+1,fontsize==10);
+ _InsertString(sizesub,sizesubpos++,"Medium",MENUITEM_SUBSIZEBASE+2,fontsize==0);
+ _InsertString(sizesub,sizesubpos++,"Smaller",MENUITEM_SUBSIZEBASE+3,fontsize==-4);
+ _InsertString(sizesub,sizesubpos++,"Smallest",MENUITEM_SUBSIZEBASE+4,fontsize==-8);
+ HMENU langsub=_InsertSub(sub,subpos++,"Language");
+ int langsubpos=0;
+ for(int i=0;;i++) {
+ const char *lang=parm->decode->getSubLanguage(i);
+ if(!lang) break;
+ _InsertString(langsub,langsubpos++,(char *)lang,MENUITEM_SUBSLANGBASE+i,i==parm->decode->getCurSubLanguage());
+ }
+ }
+#endif//WNDMENU_NOSUBTITLES
+
+#ifndef WNDMENU_NOCLOSE
+ _InsertSep(hMenu,pos++);
+ _InsertString(hMenu,pos++,"E&xit\tAlt+F4",MENUITEM_CLOSE);
+#endif//WNDMENU_NOCLOSE
+
+ POINT p;
+ GetCursorPos(&p);
+ int x=TrackPopupMenu(hMenu,TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTBUTTON|TPM_NONOTIFY,p.x,p.y,0,hwnd,NULL);
+ DestroyMenu(hMenu);
+ switch (x)
+ {
+#ifndef WNDMENU_NOABOUT
+ case MENUITEM_ABOUT:
+#ifdef _ABOUT_H_ // dont display the about box if do_about isn't there anyway
+ do_about(hwnd);
+#endif
+ break;
+#endif//WNDMENU_NOABOUT
+#ifndef WNDMENU_NOOPTIONS
+ case MENUITEM_VOVERLAYS:
+ _WriteConfigItemInt("overlays",parm->vidOut->vid_overlays=!parm->vidOut->vid_overlays);
+ PostMessage(hwnd,WM_USER+0x1,0,0); //renegotiate surface
+ break;
+ case MENUITEM_VDDRAW:
+ _WriteConfigItemInt("ddraw",parm->vidOut->vid_ddraw=!parm->vidOut->vid_ddraw);
+ PostMessage(hwnd,WM_USER+0x1,0,0); //renegotiate surface
+ break;
+ case MENUITEM_ASPECTADJ:
+ _WriteConfigItemInt("aspectadj",parm->vidOut->vid_aspectadj=!parm->vidOut->vid_aspectadj);
+ PostMessage(hwnd,WM_TIMER,1,0);
+ break;
+ case MENUITEM_VSYNC:
+ _WriteConfigItemInt("vsync",parm->vidOut->vid_vsync=!parm->vidOut->vid_vsync);
+ break;
+ case MENUITEM_SOFTVOLMIX:
+ _WriteConfigItemInt("use_mixer",g_audio_use_mixer=!g_audio_use_mixer);
+ parm->decode->setvolume(parm->decode->getvolume());
+ break;
+#endif//WNDMENU_NOOPTIONS
+
+#ifndef WNDMENU_NOZOOM
+ case MENUITEM_ZOOM50:
+ case MENUITEM_ZOOM100:
+ case MENUITEM_ZOOM200:
+ case MENUITEM_ZOOM400:
+ if (parm->vidOut->is_fullscreen()) parm->vidOut->remove_fullscreen();
+ {
+ int w=parm->decode->getWidth();
+ int h=parm->decode->getHeight();
+ if (x == MENUITEM_ZOOM50) { w/=2; h/=2; }
+ else if (x == MENUITEM_ZOOM200) { w*=2; h*=2; }
+ else if (x == MENUITEM_ZOOM400) { w*=4; h*=4; }
+ parm->vidOut->setOutputSize(w,h);
+
+ }
+ break;
+#endif//WNDMENU_NOZOOM
+#ifndef WNDMENU_NOZOOMFS
+ case MENUITEM_ZOOMFS:
+ if (parm->vidOut->is_fullscreen()) parm->vidOut->remove_fullscreen();
+ else parm->vidOut->fullscreen();
+ break;
+#endif//WNDMENU_NOZOOMFS
+#ifndef WNDMENU_NOCLOSE
+ case MENUITEM_CLOSE:
+ SendMessage(hwnd,WM_CLOSE,0,0);
+ break;
+#endif//WNDMENU_NOCLOSE
+#ifndef WNDMENU_NOSUBTITLES
+ case MENUITEM_ENABLESUBTITLES:
+ if(parm->decode) parm->decode->enableSubs(!parm->decode->subsEnabled());
+ _WriteConfigItemInt("subtitles",parm->decode->subsEnabled());
+ break;
+ case MENUITEM_SUBSIZEBASE:
+ case MENUITEM_SUBSIZEBASE+1:
+ case MENUITEM_SUBSIZEBASE+2:
+ case MENUITEM_SUBSIZEBASE+3:
+ case MENUITEM_SUBSIZEBASE+4:
+ if(parm->decode) {
+ int sizes[]={20,10,0,-4,-8};
+ int s=x-MENUITEM_SUBSIZEBASE;
+ parm->decode->setSubsFontSize(sizes[s]);
+ _WriteConfigItemInt("subtitles_size",parm->decode->getSubsFontSize());
+ }
+ break;
+#endif//WNDMENU_NOSUBTITLES
+ default:
+#ifndef WNDMENU_NOVOLUME
+ if (x >= MENUITEM_VOLUMEBASE && x <= MENUITEM_VOLUMEBASE+10)
+ {
+ int v=10-(x-MENUITEM_VOLUMEBASE);
+ parm->decode->setvolume((v*255)/10);
+ _WriteConfigItemInt("volume",parm->decode->getvolume());
+ }
+#endif
+ break;
+#ifndef WNDMENU_NOCLOSE
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ return 0;
+#endif
+ }
+#ifndef WNDMENU_NOSUBTITLES
+ if(x>=MENUITEM_SUBSLANGBASE && x<=MENUITEM_SUBSLANGBASE+64) {
+ parm->decode->setSubLanguage(x-MENUITEM_SUBSLANGBASE);
+ }
+#endif
+ }
+ }
+ return DefWindowProc(hwnd,uMsg,wParam,lParam);
+}
+
+
+#endif//_WNDMENU_H_ \ No newline at end of file
diff --git a/Src/nsv/svc_nsvFactory.cpp b/Src/nsv/svc_nsvFactory.cpp
new file mode 100644
index 00000000..dda7ce0e
--- /dev/null
+++ b/Src/nsv/svc_nsvFactory.cpp
@@ -0,0 +1 @@
+#include "svc_nsvFactory.h"
diff --git a/Src/nsv/svc_nsvFactory.h b/Src/nsv/svc_nsvFactory.h
new file mode 100644
index 00000000..ffc0ca66
--- /dev/null
+++ b/Src/nsv/svc_nsvFactory.h
@@ -0,0 +1,52 @@
+#ifndef NULLSOFT_NSV_SVC_NSVFACTORY_H
+#define NULLSOFT_NSV_SVC_NSVFACTORY_H
+
+#include <bfc/Dispatch.h>
+#include <bfc/platform/types.h>
+#include <api/service/services.h>
+class IAudioDecoder;
+class IVideoDecoder;
+class IAudioOutput;
+
+/*
+ **********************************************************************
+ * Important Note! *
+ * *
+ * *
+ * Objects created by this class are allocated using api_memmgr *
+ * You MUST call api_memmgr::Delete() to delete these objects. *
+ * Do not use the C++ delete operator *
+ * as the memory was allocated with a different heap / malloc arena *
+ * *
+ **********************************************************************
+ */
+class svc_nsvFactory : public Dispatchable
+{
+protected:
+ svc_nsvFactory(){}
+ ~svc_nsvFactory(){}
+public:
+ virtual IAudioDecoder *CreateAudioDecoder(FOURCC format, IAudioOutput **output);
+ IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int fmt, int *flip);
+
+public:
+ DISPATCH_CODES
+ {
+ SVC_NSVFACTORY_CREATEAUDIODECODER=10,
+ SVC_NSVFACTORY_CREATEVIDEODECODER=20,
+ };
+
+ static FOURCC getServiceType() { return WaSvc::NSVFACTORY; }
+};
+
+inline IAudioDecoder *svc_nsvFactory::CreateAudioDecoder(FOURCC format, IAudioOutput **output)
+{
+ return _call(SVC_NSVFACTORY_CREATEAUDIODECODER, (IAudioDecoder *)0, format, output);
+}
+
+inline IVideoDecoder *svc_nsvFactory::CreateVideoDecoder(int w, int h, double framerate, unsigned int fmt, int *flip)
+{
+ return _call(SVC_NSVFACTORY_CREATEVIDEODECODER, (IVideoDecoder *)0, w, h, framerate, fmt, flip);
+}
+
+#endif