using System;
using System.Text;
using OpenTK;

namespace SharpQuake
{
	internal static class snd
	{
		public static bool IsInitialized
		{
			get
			{
				return snd._Controller.IsInitialized;
			}
		}

		public static dma_t shm
		{
			get
			{
				return snd._shm;
			}
		}

		public static float BgmVolume
		{
			get
			{
				return snd._BgmVolume.Value;
			}
		}

		public static float Volume
		{
			get
			{
				return snd._Volume.Value;
			}
		}

		public static void Init()
		{
			Con.Print("\nSound Initialization\n", Array.Empty<object>());
			if (common.HasParam("-nosound"))
			{
				return;
			}
			for (int i = 0; i < snd._Channels.Length; i++)
			{
				snd._Channels[i] = new channel_t();
			}
			cmd.Add("play", new xcommand_t(snd.Play));
			cmd.Add("playvol", new xcommand_t(snd.PlayVol));
			cmd.Add("stopsound", new xcommand_t(snd.StopAllSoundsCmd));
			cmd.Add("soundlist", new xcommand_t(snd.SoundList));
			cmd.Add("soundinfo", new xcommand_t(snd.SoundInfo_f));
			snd._IsInitialized = true;
			snd.Startup();
			snd.InitScaletable();
			snd._NumSfx = 0;
			Con.Print("Sound sampling rate: {0}\n", new object[]
			{
				snd._shm.speed
			});
			snd._AmbientSfx[0] = snd.PrecacheSound("ambience/water1.wav");
			snd._AmbientSfx[1] = snd.PrecacheSound("ambience/wind2.wav");
			snd.StopAllSounds(true);
		}

		public static void AmbientOff()
		{
			snd._Ambient = false;
		}

		public static void AmbientOn()
		{
			snd._Ambient = true;
		}

		public static void Shutdown()
		{
			if (!snd._Controller.IsInitialized)
			{
				return;
			}
			if (snd._shm != null)
			{
				snd._shm.gamealive = false;
			}
			snd._Controller.Shutdown();
			snd._shm = null;
		}

		public static void TouchSound(string sample)
		{
			if (!snd._Controller.IsInitialized)
			{
				return;
			}
			Cache.Check(snd.FindName(sample).cache);
		}

		public static void ClearBuffer()
		{
			if (!snd._Controller.IsInitialized || snd._shm == null || snd._shm.buffer == null)
			{
				return;
			}
			snd._Controller.ClearBuffer();
		}

		public static void StaticSound(sfx_t sfx, ref Vector3 origin, float vol, float attenuation)
		{
			if (sfx == null)
			{
				return;
			}
			if (snd._TotalChannels == 128)
			{
				Con.Print("total_channels == MAX_CHANNELS\n", Array.Empty<object>());
				return;
			}
			channel_t channel_t = snd._Channels[snd._TotalChannels];
			snd._TotalChannels++;
			sfxcache_t sfxcache_t = snd.LoadSound(sfx);
			if (sfxcache_t == null)
			{
				return;
			}
			if (sfxcache_t.loopstart == -1)
			{
				Con.Print("Sound {0} not looped\n", new object[]
				{
					sfx.name
				});
				return;
			}
			channel_t.sfx = sfx;
			channel_t.origin = origin;
			channel_t.master_vol = (int)vol;
			channel_t.dist_mult = attenuation / 64f / snd._SoundNominalClipDist;
			channel_t.end = snd._PaintedTime + sfxcache_t.length;
			snd.Spatialize(channel_t);
		}

		public static void StartSound(int entnum, int entchannel, sfx_t sfx, ref Vector3 origin, float fvol, float attenuation)
		{
			if (!snd._SoundStarted || sfx == null)
			{
				return;
			}
			if (snd._NoSound.Value != 0f)
			{
				return;
			}
			int master_vol = (int)(fvol * 255f);
			channel_t channel_t = snd.PickChannel(entnum, entchannel);
			if (channel_t == null)
			{
				return;
			}
			channel_t.origin = origin;
			channel_t.dist_mult = attenuation / snd._SoundNominalClipDist;
			channel_t.master_vol = master_vol;
			channel_t.entnum = entnum;
			channel_t.entchannel = entchannel;
			snd.Spatialize(channel_t);
			if (channel_t.leftvol == 0 && channel_t.rightvol == 0)
			{
				return;
			}
			sfxcache_t sfxcache_t = snd.LoadSound(sfx);
			if (sfxcache_t == null)
			{
				channel_t.sfx = null;
				return;
			}
			channel_t.sfx = sfx;
			channel_t.pos = 0;
			channel_t.end = snd._PaintedTime + sfxcache_t.length;
			for (int i = 4; i < 12; i++)
			{
				channel_t channel_t2 = snd._Channels[i];
				if (channel_t2 != channel_t && channel_t2.sfx == sfx && channel_t2.pos == 0)
				{
					int num = sys.Random((int)(0.1 * (double)snd._shm.speed));
					if (num >= channel_t.end)
					{
						num = channel_t.end - 1;
					}
					channel_t.pos += num;
					channel_t.end -= num;
					return;
				}
			}
		}

		public static void StopSound(int entnum, int entchannel)
		{
			for (int i = 0; i < 8; i++)
			{
				if (snd._Channels[i].entnum == entnum && snd._Channels[i].entchannel == entchannel)
				{
					snd._Channels[i].end = 0;
					snd._Channels[i].sfx = null;
					return;
				}
			}
		}

		public static sfx_t PrecacheSound(string sample)
		{
			if (!snd._IsInitialized || snd._NoSound.Value != 0f)
			{
				return null;
			}
			sfx_t sfx_t = snd.FindName(sample);
			if (snd._Precache.Value != 0f)
			{
				snd.LoadSound(sfx_t);
			}
			return sfx_t;
		}

		public static void ClearPrecache()
		{
		}

		public static void Update(ref Vector3 origin, ref Vector3 forward, ref Vector3 right, ref Vector3 up)
		{
			if (!snd._IsInitialized || snd._SoundBlocked > 0)
			{
				return;
			}
			snd._ListenerOrigin = origin;
			snd._ListenerForward = forward;
			snd._ListenerRight = right;
			snd._ListenerUp = up;
			snd.UpdateAmbientSounds();
			channel_t channel_t = null;
			for (int i = 4; i < snd._TotalChannels; i++)
			{
				channel_t channel_t2 = snd._Channels[i];
				if (channel_t2.sfx != null)
				{
					snd.Spatialize(channel_t2);
					if ((channel_t2.leftvol != 0 || channel_t2.rightvol != 0) && i >= 12)
					{
						if (channel_t != null && channel_t.sfx == channel_t2.sfx)
						{
							channel_t.leftvol += channel_t2.leftvol;
							channel_t.rightvol += channel_t2.rightvol;
							channel_t2.leftvol = (channel_t2.rightvol = 0);
						}
						else
						{
							channel_t = snd._Channels[12];
							int j;
							for (j = 12; j < i; j++)
							{
								channel_t = snd._Channels[j];
								if (channel_t.sfx == channel_t2.sfx)
								{
									break;
								}
							}
							if (j == snd._TotalChannels)
							{
								channel_t = null;
							}
							else if (channel_t != channel_t2)
							{
								channel_t.leftvol += channel_t2.leftvol;
								channel_t.rightvol += channel_t2.rightvol;
								channel_t2.leftvol = (channel_t2.rightvol = 0);
							}
						}
					}
				}
			}
			if (snd._Show.Value != 0f)
			{
				int num = 0;
				for (int k = 0; k < snd._TotalChannels; k++)
				{
					channel_t channel_t3 = snd._Channels[k];
					if (channel_t3.sfx != null && (channel_t3.leftvol > 0 || channel_t3.rightvol > 0))
					{
						num++;
					}
				}
				Con.Print("----({0})----\n", new object[]
				{
					num
				});
			}
			snd.Update();
		}

		public static void StopAllSounds(bool clear)
		{
			if (!snd._Controller.IsInitialized)
			{
				return;
			}
			snd._TotalChannels = 12;
			for (int i = 0; i < 128; i++)
			{
				if (snd._Channels[i].sfx != null)
				{
					snd._Channels[i].Clear();
				}
			}
			if (clear)
			{
				snd.ClearBuffer();
			}
		}

		public static void BeginPrecaching()
		{
		}

		public static void EndPrecaching()
		{
		}

		public static void ExtraUpdate()
		{
			if (!snd._IsInitialized)
			{
				return;
			}
			if (snd._NoExtraUpdate.Value != 0f)
			{
				return;
			}
			snd.Update();
		}

		public static void LocalSound(string sound)
		{
			if (snd._NoSound.Value != 0f)
			{
				return;
			}
			if (!snd._Controller.IsInitialized)
			{
				return;
			}
			sfx_t sfx_t = snd.PrecacheSound(sound);
			if (sfx_t == null)
			{
				Con.Print("S_LocalSound: can't cache {0}\n", new object[]
				{
					sound
				});
				return;
			}
			snd.StartSound(client.cl.viewentity, -1, sfx_t, ref common.ZeroVector, 1f, 1f);
		}

		public static void Startup()
		{
			if (snd._IsInitialized && !snd._Controller.IsInitialized)
			{
				snd._Controller.Init();
				snd._SoundStarted = snd._Controller.IsInitialized;
			}
		}

		public static void BlockSound()
		{
			snd._SoundBlocked++;
			if (snd._SoundBlocked == 1)
			{
				snd._Controller.ClearBuffer();
			}
		}

		public static void UnblockSound()
		{
			snd._SoundBlocked--;
		}

		private static void Play()
		{
			for (int i = 1; i < cmd.Argc; i++)
			{
				string text = cmd.Argv(i);
				if (text.IndexOf('.') == -1)
				{
					text += ".wav";
				}
				sfx_t sfx = snd.PrecacheSound(text);
				snd.StartSound(snd._PlayHash++, 0, sfx, ref snd._ListenerOrigin, 1f, 1f);
			}
		}

		private static void PlayVol()
		{
			for (int i = 1; i < cmd.Argc; i += 2)
			{
				string text = cmd.Argv(i);
				if (text.IndexOf('.') == -1)
				{
					text += ".wav";
				}
				sfx_t sfx = snd.PrecacheSound(text);
				float fvol = float.Parse(cmd.Argv(i + 1));
				snd.StartSound(snd._PlayVolHash++, 0, sfx, ref snd._ListenerOrigin, fvol, 1f);
			}
		}

		private static void SoundList()
		{
			int num = 0;
			for (int i = 0; i < snd._NumSfx; i++)
			{
				sfx_t sfx_t = snd._KnownSfx[i];
				sfxcache_t sfxcache_t = (sfxcache_t)Cache.Check(sfx_t.cache);
				if (sfxcache_t != null)
				{
					int num2 = sfxcache_t.length * sfxcache_t.width * (sfxcache_t.stereo + 1);
					num += num2;
					if (sfxcache_t.loopstart >= 0)
					{
						Con.Print("L", Array.Empty<object>());
					}
					else
					{
						Con.Print(" ", Array.Empty<object>());
					}
					Con.Print("({0:d2}b) {1:g6} : {2}\n", new object[]
					{
						sfxcache_t.width * 8,
						num2,
						sfx_t.name
					});
				}
			}
			Con.Print("Total resident: {0}\n", new object[]
			{
				num
			});
		}

		private static void SoundInfo_f()
		{
			if (!snd._Controller.IsInitialized || snd._shm == null)
			{
				Con.Print("sound system not started\n", Array.Empty<object>());
				return;
			}
			Con.Print("{0:d5} stereo\n", new object[]
			{
				snd._shm.channels - 1
			});
			Con.Print("{0:d5} samples\n", new object[]
			{
				snd._shm.samples
			});
			Con.Print("{0:d5} samplepos\n", new object[]
			{
				snd._shm.samplepos
			});
			Con.Print("{0:d5} samplebits\n", new object[]
			{
				snd._shm.samplebits
			});
			Con.Print("{0:d5} submission_chunk\n", new object[]
			{
				snd._shm.submission_chunk
			});
			Con.Print("{0:d5} speed\n", new object[]
			{
				snd._shm.speed
			});
			Con.Print("{0:d5} total_channels\n", new object[]
			{
				snd._TotalChannels
			});
		}

		private static void StopAllSoundsCmd()
		{
			snd.StopAllSounds(true);
		}

		private static sfx_t FindName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				sys.Error("S_FindName: NULL or empty\n", Array.Empty<object>());
			}
			if (name.Length >= 64)
			{
				sys.Error("Sound name too long: {0}", new object[]
				{
					name
				});
			}
			for (int i = 0; i < snd._NumSfx; i++)
			{
				if (snd._KnownSfx[i].name == name)
				{
					return snd._KnownSfx[i];
				}
			}
			if (snd._NumSfx == 512)
			{
				sys.Error("S_FindName: out of sfx_t", Array.Empty<object>());
			}
			sfx_t sfx_t = snd._KnownSfx[snd._NumSfx];
			sfx_t.name = name;
			snd._NumSfx++;
			return sfx_t;
		}

		private static void Spatialize(channel_t ch)
		{
			if (ch.entnum == client.cl.viewentity)
			{
				ch.leftvol = ch.master_vol;
				ch.rightvol = ch.master_vol;
				return;
			}
			sfx_t sfx = ch.sfx;
			Vector3 right = ch.origin - snd._ListenerOrigin;
			float num = mathlib.Normalize(ref right) * ch.dist_mult;
			float num2 = Vector3.Dot(snd._ListenerRight, right);
			float num3;
			float num4;
			if (snd._shm.channels == 1)
			{
				num3 = 1f;
				num4 = 1f;
			}
			else
			{
				num3 = 1f + num2;
				num4 = 1f - num2;
			}
			float num5 = (1f - num) * num3;
			ch.rightvol = (int)((float)ch.master_vol * num5);
			if (ch.rightvol < 0)
			{
				ch.rightvol = 0;
			}
			num5 = (1f - num) * num4;
			ch.leftvol = (int)((float)ch.master_vol * num5);
			if (ch.leftvol < 0)
			{
				ch.leftvol = 0;
			}
		}

		private static sfxcache_t LoadSound(sfx_t s)
		{
			sfxcache_t sfxcache_t = (sfxcache_t)Cache.Check(s.cache);
			if (sfxcache_t != null)
			{
				return sfxcache_t;
			}
			string text = "sound/" + s.name;
			byte[] array = common.LoadFile(text);
			if (array == null)
			{
				Con.Print("Couldn't load {0}\n", new object[]
				{
					text
				});
				return null;
			}
			wavinfo_t wavInfo = snd.GetWavInfo(s.name, array);
			if (wavInfo.channels != 1)
			{
				Con.Print("{0} is a stereo sample\n", new object[]
				{
					s.name
				});
				return null;
			}
			float num = (float)wavInfo.rate / (float)snd._shm.speed;
			int num2 = (int)((float)wavInfo.samples / num);
			num2 *= wavInfo.width * wavInfo.channels;
			s.cache = Cache.Alloc(num2, s.name);
			if (s.cache == null)
			{
				return null;
			}
			sfxcache_t = new sfxcache_t();
			sfxcache_t.length = wavInfo.samples;
			sfxcache_t.loopstart = wavInfo.loopstart;
			sfxcache_t.speed = wavInfo.rate;
			sfxcache_t.width = wavInfo.width;
			sfxcache_t.stereo = wavInfo.channels;
			s.cache.data = sfxcache_t;
			snd.ResampleSfx(s, sfxcache_t.speed, sfxcache_t.width, new ByteArraySegment(array, wavInfo.dataofs));
			return sfxcache_t;
		}

		private static channel_t PickChannel(int entnum, int entchannel)
		{
			int num = -1;
			int num2 = int.MaxValue;
			for (int i = 4; i < 12; i++)
			{
				if (entchannel != 0 && snd._Channels[i].entnum == entnum && (snd._Channels[i].entchannel == entchannel || entchannel == -1))
				{
					num = i;
					break;
				}
				if ((snd._Channels[i].entnum != client.cl.viewentity || entnum == client.cl.viewentity || snd._Channels[i].sfx == null) && snd._Channels[i].end - snd._PaintedTime < num2)
				{
					num2 = snd._Channels[i].end - snd._PaintedTime;
					num = i;
				}
			}
			if (num == -1)
			{
				return null;
			}
			if (snd._Channels[num].sfx != null)
			{
				snd._Channels[num].sfx = null;
			}
			return snd._Channels[num];
		}

		private static void UpdateAmbientSounds()
		{
			if (!snd._Ambient)
			{
				return;
			}
			if (client.cl.worldmodel == null)
			{
				return;
			}
			mleaf_t mleaf_t = Mod.PointInLeaf(ref snd._ListenerOrigin, client.cl.worldmodel);
			if (mleaf_t == null || snd._AmbientLevel.Value == 0f)
			{
				for (int i = 0; i < 4; i++)
				{
					snd._Channels[i].sfx = null;
				}
				return;
			}
			for (int j = 0; j < 4; j++)
			{
				channel_t channel_t = snd._Channels[j];
				channel_t.sfx = snd._AmbientSfx[j];
				float num = snd._AmbientLevel.Value * (float)mleaf_t.ambient_sound_level[j];
				if (num < 8f)
				{
					num = 0f;
				}
				if ((float)channel_t.master_vol < num)
				{
					channel_t.master_vol += (int)(host.FrameTime * (double)snd._AmbientFade.Value);
					if ((float)channel_t.master_vol > num)
					{
						channel_t.master_vol = (int)num;
					}
				}
				else if ((float)channel_t.master_vol > num)
				{
					channel_t.master_vol -= (int)(host.FrameTime * (double)snd._AmbientFade.Value);
					if ((float)channel_t.master_vol < num)
					{
						channel_t.master_vol = (int)num;
					}
				}
				channel_t.leftvol = (channel_t.rightvol = channel_t.master_vol);
			}
		}

		private static void Update()
		{
			if (!snd._SoundStarted || snd._SoundBlocked > 0)
			{
				return;
			}
			snd.GetSoundTime();
			if (snd._PaintedTime < snd._SoundTime)
			{
				snd._PaintedTime = snd._SoundTime;
			}
			int num = (int)((float)snd._SoundTime + snd._MixAhead.Value * (float)snd._shm.speed);
			int num2 = snd._shm.samples >> snd._shm.channels - 1;
			if (num - snd._SoundTime > num2)
			{
				num = snd._SoundTime + num2;
			}
			snd.PaintChannels(num);
		}

		private static void GetSoundTime()
		{
			int num = snd._shm.samples / snd._shm.channels;
			int position = snd._Controller.GetPosition();
			if (position < snd._OldSamplePos)
			{
				snd._Buffers++;
				if (snd._PaintedTime > 1073741824)
				{
					snd._Buffers = 0;
					snd._PaintedTime = num;
					snd.StopAllSounds(true);
				}
			}
			snd._OldSamplePos = position;
			snd._SoundTime = snd._Buffers * num + position / snd._shm.channels;
		}

		static snd()
		{
			for (int i = 0; i < snd._KnownSfx.Length; i++)
			{
				snd._KnownSfx[i] = new sfx_t();
			}
		}

		private static wavinfo_t GetWavInfo(string name, byte[] wav)
		{
			wavinfo_t wavinfo_t = new wavinfo_t();
			if (wav == null)
			{
				return wavinfo_t;
			}
			WavHelper wavHelper = new WavHelper(wav);
			int num = 0;
			if (wavHelper.FindChunk("RIFF", num) == -1)
			{
				Con.Print("Missing RIFF chunk\n", Array.Empty<object>());
				return wavinfo_t;
			}
			if (Encoding.ASCII.GetString(wav, num + 8, 4) != "WAVE")
			{
				Con.Print("RIFF chunk is not WAVE\n", Array.Empty<object>());
				return wavinfo_t;
			}
			num += 12;
			int num2 = wavHelper.FindChunk("fmt ", num);
			if (num2 == -1)
			{
				Con.Print("Missing fmt chunk\n", Array.Empty<object>());
				return wavinfo_t;
			}
			if (wavHelper.GetLittleShort(num2 + 8) != 1)
			{
				Con.Print("Microsoft PCM format only\n", Array.Empty<object>());
				return wavinfo_t;
			}
			wavinfo_t.channels = (int)wavHelper.GetLittleShort(num2 + 10);
			wavinfo_t.rate = wavHelper.GetLittleLong(num2 + 12);
			wavinfo_t.width = (int)(wavHelper.GetLittleShort(num2 + 16 + 4 + 2) / 8);
			int num3 = wavHelper.FindChunk("cue ", num);
			if (num3 != -1)
			{
				wavinfo_t.loopstart = wavHelper.GetLittleLong(num3 + 32);
				int num4 = wavHelper.FindChunk("LIST", num3);
				if (num4 != -1 && Encoding.ASCII.GetString(wav, num4 + 28, 4) == "mark")
				{
					int littleLong = wavHelper.GetLittleLong(num4 + 24);
					wavinfo_t.samples = wavinfo_t.loopstart + littleLong;
				}
			}
			else
			{
				wavinfo_t.loopstart = -1;
			}
			int num5 = wavHelper.FindChunk("data", num);
			if (num5 == -1)
			{
				Con.Print("Missing data chunk\n", Array.Empty<object>());
				return wavinfo_t;
			}
			int num6 = wavHelper.GetLittleLong(num5 + 4) / wavinfo_t.width;
			if (wavinfo_t.samples > 0)
			{
				if (num6 < wavinfo_t.samples)
				{
					sys.Error("Sound {0} has a bad loop length", new object[]
					{
						name
					});
				}
			}
			else
			{
				wavinfo_t.samples = num6;
			}
			wavinfo_t.dataofs = num5 + 8;
			return wavinfo_t;
		}

		private static void ResampleSfx(sfx_t sfx, int inrate, int inwidth, ByteArraySegment data)
		{
			sfxcache_t sfxcache_t = (sfxcache_t)Cache.Check(sfx.cache);
			if (sfxcache_t == null)
			{
				return;
			}
			float num = (float)inrate / (float)snd._shm.speed;
			int num2 = (int)((float)sfxcache_t.length / num);
			sfxcache_t.length = num2;
			if (sfxcache_t.loopstart != -1)
			{
				sfxcache_t.loopstart = (int)((float)sfxcache_t.loopstart / num);
			}
			sfxcache_t.speed = snd._shm.speed;
			if (snd._LoadAs8bit.Value != 0f)
			{
				sfxcache_t.width = 1;
			}
			else
			{
				sfxcache_t.width = inwidth;
			}
			sfxcache_t.stereo = 0;
			sfxcache_t.data = new byte[num2 * sfxcache_t.width];
			byte[] data2 = data.Data;
			if (num == 1f && inwidth == 1 && sfxcache_t.width == 1)
			{
				for (int i = 0; i < num2; i++)
				{
					int num3 = (int)(data2[data.StartIndex + i] - 128);
					sfxcache_t.data[i] = (byte)((sbyte)num3);
				}
				return;
			}
			int num4 = 0;
			int num5 = (int)(num * 256f);
			short[] array = new short[1];
			for (int j = 0; j < num2; j++)
			{
				int num6 = num4 >> 8;
				num4 += num5;
				int num7;
				if (inwidth == 2)
				{
					Buffer.BlockCopy(data2, data.StartIndex + num6 * 2, array, 0, 2);
					num7 = (int)common.LittleShort(array[0]);
				}
				else
				{
					num7 = (int)(data2[data.StartIndex + num6] - 128) << 8;
				}
				if (sfxcache_t.width == 2)
				{
					array[0] = (short)num7;
					Buffer.BlockCopy(array, 0, sfxcache_t.data, j * 2, 2);
				}
				else
				{
					sfxcache_t.data[j] = (byte)((sbyte)(num7 >> 8));
				}
			}
		}

		private static void InitScaletable()
		{
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 256; j++)
				{
					snd._ScaleTable[i, j] = (int)((sbyte)j) * i * 8;
				}
			}
		}

		private static void PaintChannels(int endtime)
		{
			while (snd._PaintedTime < endtime)
			{
				int num = endtime;
				if (endtime - snd._PaintedTime > 512)
				{
					num = snd._PaintedTime + 512;
				}
				Array.Clear(snd._PaintBuffer, 0, num - snd._PaintedTime);
				for (int i = 0; i < snd._TotalChannels; i++)
				{
					channel_t channel_t = snd._Channels[i];
					if (channel_t.sfx != null && (channel_t.leftvol != 0 || channel_t.rightvol != 0))
					{
						sfxcache_t sfxcache_t = snd.LoadSound(channel_t.sfx);
						if (sfxcache_t != null)
						{
							int j = snd._PaintedTime;
							while (j < num)
							{
								int num2;
								if (channel_t.end < num)
								{
									num2 = channel_t.end - j;
								}
								else
								{
									num2 = num - j;
								}
								if (num2 > 0)
								{
									if (sfxcache_t.width == 1)
									{
										snd.PaintChannelFrom8(channel_t, sfxcache_t, num2);
									}
									else
									{
										snd.PaintChannelFrom16(channel_t, sfxcache_t, num2);
									}
									j += num2;
								}
								if (j >= channel_t.end)
								{
									if (sfxcache_t.loopstart < 0)
									{
										channel_t.sfx = null;
										break;
									}
									channel_t.pos = sfxcache_t.loopstart;
									channel_t.end = j + sfxcache_t.length - channel_t.pos;
								}
							}
						}
					}
				}
				snd.TransferPaintBuffer(num);
				snd._PaintedTime = num;
			}
		}

		private static void PaintChannelFrom8(channel_t ch, sfxcache_t sc, int count)
		{
			if (ch.leftvol > 255)
			{
				ch.leftvol = 255;
			}
			if (ch.rightvol > 255)
			{
				ch.rightvol = 255;
			}
			int num = ch.leftvol >> 3;
			int num2 = ch.rightvol >> 3;
			byte[] data = sc.data;
			int pos = ch.pos;
			for (int i = 0; i < count; i++)
			{
				int num3 = (int)data[pos + i];
				portable_samplepair_t[] paintBuffer = snd._PaintBuffer;
				int num4 = i;
				paintBuffer[num4].left = paintBuffer[num4].left + snd._ScaleTable[num, num3];
				portable_samplepair_t[] paintBuffer2 = snd._PaintBuffer;
				int num5 = i;
				paintBuffer2[num5].right = paintBuffer2[num5].right + snd._ScaleTable[num2, num3];
			}
			ch.pos += count;
		}

		private static void PaintChannelFrom16(channel_t ch, sfxcache_t sc, int count)
		{
			int leftvol = ch.leftvol;
			int rightvol = ch.rightvol;
			byte[] data = sc.data;
			int num = ch.pos * 2;
			for (int i = 0; i < count; i++)
			{
				short num2 = (short)((int)data[num] + ((int)data[num + 1] << 8));
				int num3 = (int)num2 * leftvol >> 8;
				int num4 = (int)num2 * rightvol >> 8;
				portable_samplepair_t[] paintBuffer = snd._PaintBuffer;
				int num5 = i;
				paintBuffer[num5].left = paintBuffer[num5].left + num3;
				portable_samplepair_t[] paintBuffer2 = snd._PaintBuffer;
				int num6 = i;
				paintBuffer2[num6].right = paintBuffer2[num6].right + num4;
				num += 2;
			}
			ch.pos += count;
		}

		private static void TransferPaintBuffer(int endtime)
		{
			if (snd._shm.samplebits == 16 && snd._shm.channels == 2)
			{
				snd.TransferStereo16(endtime);
				return;
			}
			int num = (endtime - snd._PaintedTime) * snd._shm.channels;
			int num2 = snd._shm.samples - 1;
			int num3 = 0;
			int channels = snd._shm.channels;
			int num4 = (int)(snd._Volume.Value * 256f);
			byte[] array = snd._Controller.LockBuffer();
			Union4b empty = Union4b.Empty;
			int num5 = 0;
			bool flag = true;
			int count = num * (snd._shm.samplebits >> 3) & num2;
			if (snd._shm.samplebits == 16)
			{
				while (num-- > 0)
				{
					int num6;
					if (flag)
					{
						num6 = snd._PaintBuffer[num5].left * num4 >> 8;
					}
					else
					{
						num6 = snd._PaintBuffer[num5].right * num4 >> 8;
					}
					if (num6 > 32767)
					{
						num6 = 32767;
					}
					else if (num6 < -32768)
					{
						num6 = -32768;
					}
					empty.i0 = num6;
					array[num3 * 2] = empty.b0;
					array[num3 * 2 + 1] = empty.b1;
					if (snd._shm.channels == 2 && flag)
					{
						flag = false;
						num3 += 2;
					}
					else
					{
						flag = true;
						num5++;
						num3 = (num3 + 1 & num2);
					}
				}
			}
			else if (snd._shm.samplebits == 8)
			{
				while (num-- > 0)
				{
					int num6;
					if (flag)
					{
						num6 = snd._PaintBuffer[num5].left * num4 >> 8;
					}
					else
					{
						num6 = snd._PaintBuffer[num5].right * num4 >> 8;
					}
					if (num6 > 32767)
					{
						num6 = 32767;
					}
					else if (num6 < -32768)
					{
						num6 = -32768;
					}
					array[num3] = (byte)((num6 >> 8) + 128);
					num3 = (num3 + 1 & num2);
					if (snd._shm.channels == 2 && flag)
					{
						flag = false;
					}
					else
					{
						flag = true;
						num5++;
					}
				}
			}
			snd._Controller.UnlockBuffer(count);
		}

		private static void TransferStereo16(int endtime)
		{
			int num = (int)(snd._Volume.Value * 256f);
			int i = snd._PaintedTime;
			byte[] array = snd._Controller.LockBuffer();
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			Union4b empty = Union4b.Empty;
			while (i < endtime)
			{
				int num5 = i & (snd._shm.samples >> 1) - 1;
				int num6 = (snd._shm.samples >> 1) - num5;
				if (i + num6 > endtime)
				{
					num6 = endtime - i;
				}
				for (int j = 0; j < num6; j++)
				{
					int num7 = snd._PaintBuffer[num2 + j].left * num >> 8;
					int num8 = snd._PaintBuffer[num2 + j].right * num >> 8;
					if (num7 > 32767)
					{
						num7 = 32767;
					}
					else if (num7 < -32768)
					{
						num7 = -32768;
					}
					if (num8 > 32767)
					{
						num8 = 32767;
					}
					else if (num8 < -32768)
					{
						num8 = -32768;
					}
					empty.s0 = (short)num7;
					empty.s1 = (short)num8;
					array[num4] = empty.b0;
					array[num4 + 1] = empty.b1;
					array[num4 + 2] = empty.b2;
					array[num4 + 3] = empty.b3;
					num4 += 4;
				}
				num3 += num6 * 4;
				num2 += num6;
				i += num6;
			}
			snd._Controller.UnlockBuffer(num3);
		}

		public const int DEFAULT_SOUND_PACKET_VOLUME = 255;

		public const float DEFAULT_SOUND_PACKET_ATTENUATION = 1f;

		public const int MAX_CHANNELS = 128;

		public const int MAX_DYNAMIC_CHANNELS = 8;

		private const int MAX_SFX = 512;

		private static cvar _BgmVolume = new cvar("bgmvolume", "1", true);

		private static cvar _Volume = new cvar("volume", "0.7", true);

		private static cvar _NoSound = new cvar("nosound", "0");

		private static cvar _Precache = new cvar("precache", "1");

		private static cvar _LoadAs8bit = new cvar("loadas8bit", "0");

		private static cvar _BgmBuffer = new cvar("bgmbuffer", "4096");

		private static cvar _AmbientLevel = new cvar("ambient_level", "0.3");

		private static cvar _AmbientFade = new cvar("ambient_fade", "100");

		private static cvar _NoExtraUpdate = new cvar("snd_noextraupdate", "0");

		private static cvar _Show = new cvar("snd_show", "0");

		private static cvar _MixAhead = new cvar("_snd_mixahead", "0.1", true);

		private static ISoundController _Controller = new OpenALController();

		private static bool _IsInitialized;

		private static sfx_t[] _KnownSfx = new sfx_t[512];

		private static int _NumSfx;

		private static sfx_t[] _AmbientSfx = new sfx_t[4];

		private static bool _Ambient = true;

		private static dma_t _shm = new dma_t();

		private static channel_t[] _Channels = new channel_t[128];

		private static int _TotalChannels;

		private static float _SoundNominalClipDist = 1000f;

		private static Vector3 _ListenerOrigin;

		private static Vector3 _ListenerForward;

		private static Vector3 _ListenerRight;

		private static Vector3 _ListenerUp;

		private static int _SoundTime;

		private static int _PaintedTime;

		private static bool _SoundStarted;

		private static int _SoundBlocked = 0;

		private static int _OldSamplePos;

		private static int _Buffers;

		private static int _PlayHash = 345;

		private static int _PlayVolHash = 543;

		private const int PAINTBUFFER_SIZE = 512;

		private const short C8000 = -32768;

		private static int[,] _ScaleTable = new int[32, 256];

		private static portable_samplepair_t[] _PaintBuffer = new portable_samplepair_t[512];
	}
}
